├── .gitignore ├── LICENSE.txt ├── README.md ├── azure-pipelines.yml ├── images ├── cop-file-issues.png ├── sonarqube-issues-in-code.png ├── sqldev-check-result.png ├── sqldev-check.png └── sqldev-preferences.png ├── install_tvdcc_libs.sh ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── trivadis │ │ ├── sonar │ │ └── plugin │ │ │ ├── GLPValidatorConfig.java │ │ │ ├── PlsqlCustomPlugin.java │ │ │ └── TrivadisGuidelines3PlusValidatorConfig.java │ │ └── tvdcc │ │ ├── generators │ │ ├── GLPGenmodel.java │ │ ├── GenUtil.java │ │ └── TrivadisGuidelines3PlusGenmodel.java │ │ └── validators │ │ ├── GLP.java │ │ ├── Hint.java │ │ ├── OverrideTrivadisGuidelines.java │ │ ├── SQLInjection.java │ │ ├── TrivadisGuidelines3Plus.java │ │ └── TrivadisPlsqlNaming.java └── resources │ ├── GLP │ ├── genmodel │ │ ├── plsqlcop-model.xml │ │ └── rules.xml │ └── sample │ │ ├── guideline_9001.sql │ │ ├── guideline_9002.sql │ │ └── guideline_9003.sql │ └── TrivadisGuidelines3Plus │ ├── genmodel │ ├── plsqlcop-model.xml │ └── rules.xml │ └── sample │ ├── guideline_9101.sql │ ├── guideline_9102.sql │ ├── guideline_9103.sql │ ├── guideline_9104.sql │ ├── guideline_9105.sql │ ├── guideline_9106.sql │ ├── guideline_9107.sql │ ├── guideline_9108.sql │ ├── guideline_9109.sql │ ├── guideline_9110.sql │ ├── guideline_9111.sql │ ├── guideline_9112.sql │ ├── guideline_9113.sql │ ├── guideline_9114.sql │ ├── guideline_9115.sql │ ├── guideline_9501.sql │ ├── guideline_9600.sql │ ├── guideline_9601.sql │ ├── guideline_9602.sql │ ├── guideline_9603.sql │ ├── guideline_9604.sql │ └── guideline_9605.sql └── test └── java └── com └── trivadis └── tvdcc └── validators └── tests ├── AbstractValidatorTest.java ├── GLPTest.java ├── HintTest.java ├── OverrideTrivadisGuidelinesTest.java ├── SQLInjectionTest.java ├── TrivadisGuidelines3PlusTest.java ├── TrivadisPlsqlNamingPropertiesFileTest.java ├── TrivadisPlsqlNamingSystemPropertiesTest.java └── TrivadisPlsqlNamingTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *._trace 3 | *.xtendbin 4 | *.DS_Store 5 | *.log 6 | tvdcc_report.* 7 | code_coverage.html 8 | **/target 9 | **/bin 10 | **/test-output 11 | **/xtend-gen 12 | **/xtext-gen 13 | **/.sonar 14 | **/.project 15 | **/.classpath 16 | **/.settings 17 | **/.scannerwork 18 | **/.idea 19 | **/.vscode 20 | 21 | # Mobile Tools for Java (J2ME) 22 | .mtj.tmp/ 23 | 24 | # Package Files # 25 | *.jar 26 | *.war 27 | *.ear 28 | 29 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 30 | hs_err_pid* 31 | 32 | /.metadata/ 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 2 | Unported License (the "License"); you may not use this file except 3 | in compliance with the License. You may obtain a copy of the License at 4 | 5 | https://creativecommons.org/licenses/by-nc-nd/3.0/ 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # db* CODECOP Validators 2 | 3 | db* CODECOP supports custom validators. A validator must implement the `PLSQLCopValidator` Java interface and has to be a direct or indirect descendant of the `PLSQLValidator` class. Such a class can be used in the command line utility and the SQL Developer extension. 4 | 5 | For SonarQube a `ValidationConfig` is required. A config defines the validator with its rules and quality profile for SonarQube. See [GLPValidatorConfig](src/main/java/com/trivadis/sonar/plugin/GLPValidatorConfig.java). The referenced XML files are generated based on the validator and the optional [sample guidelines](src/main/resources/GLP/sample). 6 | 7 | You may use these validators as is or amend/extend them to suit your needs. 8 | 9 | ## Provided Validators 10 | 11 | This project provides the following four custom validators in the package `com.trivadis.tvdcc.validators`: 12 | 13 | Class | Description 14 | ----- | ----------- 15 | TrivadisPlsqlNaming | Checks [Naming Conventions](https://trivadis.github.io/plsql-and-sql-coding-guidelines/v4.4/2-naming-conventions/naming-conventions/#naming-conventions-for-plsql) of the Trivadis PL/SQL & SQL Coding Guidelines 16 | GLP | Checks naming of global and local variables and parameters 17 | SQLInjection | Looks for SQL injection vulnerabilities, e.g. unasserted parameters in dynamic SQL 18 | Hint | Looks for unknown hints and invalid table references 19 | OverrideTrivadisGuidelines | Extends TrivadisGuidelines3 and overrides check for [G-1050](https://trivadis.github.io/plsql-and-sql-coding-guidelines/v4.4/4-language-usage/1-general/g-1050/). 20 | TrivadisGuidelines3Plus | Combines the validators TrivadisPlsqlNaming, SQLInjection and OverrideTrivadisGuidelines. 21 | 22 | ### TrivadisPlsqlNaming 23 | 24 | This validator implements 15 guidelines to cover the chapter [2.2 Naming Conventions](https://trivadis.github.io/plsql-and-sql-coding-guidelines/v4.4/2-naming-conventions/naming-conventions/#naming-conventions-for-plsql) of the Trivadis PL/SQL & SQL Coding Guidelines. 25 | 26 | Guideline | Message 27 | --------- | ----------- 28 | G-9101 | Always name global variables to match '^g_.+$'. 29 | G-9102 | Always name local variables to match '^l_.+$'. 30 | G-9103 | Always name cursors to match '^c_.+$'. 31 | G-9104 | Always name records to match '^r_.+$'. 32 | G-9105 | Always name collection types (arrays/tables) to match '^t_.+$'. 33 | G-9106 | Always name objects to match '^o_.+$'. 34 | G-9107 | Always name cursor parameters to match '^p_.+$'. 35 | G-9108 | Always name in parameters to match '^in_.+$'. 36 | G-9109 | Always name out parameters to match '^out_.+$'. 37 | G-9110 | Always name in/out parameters to match '^io_.+$'. 38 | G-9111 | Always name record type definitions to match '^r_.+_type$'. 39 | G-9112 | Always name collection type definitions (arrays/tables) to match '^t_.+_type$'. 40 | G-9113 | Always name exceptions to match '^e_.+$'. 41 | G-9114 | Always name constants to match '^co_.+$'. 42 | G-9115 | Always name subtypes to match '^.+_type$'. 43 | 44 | These regular expressions can be customized by using a `TrivadisPlsqlNaming.properties` file. This file must be placed in the user's home directory (`$HOME` for Linux or macOS and `%HOMEDRIVE%%HOMEPATH%` for Windows). If a property is omitted it will fall back to the default value (see table above). Furthermore, you can use Java system properties to configure the naming conventions, e.g. `-DREGEX_CONSTANT_NAME=^k_.+$`. However, the `TrivadisPlsqlNaming.properties` file overrides system properties that where set when starting the JVM. 45 | 46 | Here's an example of the `TrivadisPlsqlNaming.properties` file content using default values for all properties: 47 | 48 | ``` 49 | # Override default for TrivadisPlsqlNaming validator 50 | REGEX_GLOBAL_VARIABLE_NAME = ^g_.+$ 51 | REGEX_LOCAL_VARIABLE_NAME = ^l_.+$ 52 | REGEX_CURSOR_NAME = ^c_.+$ 53 | REGEX_RECORD_NAME = ^r_.+$ 54 | REGEX_ARRAY_NAME = ^t_.+$ 55 | REGEX_OBJECT_NAME = ^o_.+$ 56 | REGEX_CURSOR_PARAMETER_NAME = ^p_.+$ 57 | REGEX_IN_PARAMETER_NAME = ^in_.+$ 58 | REGEX_OUT_PARAMETER_NAME = ^out_.+$ 59 | REGEX_IN_OUT_PARAMETER_NAME = ^io_.+$ 60 | REGEX_RECORD_TYPE_NAME = ^r_.+_type$ 61 | REGEX_ARRAY_TYPE_NAME = ^t_.+_type$ 62 | REGEX_EXCEPTION_NAME = ^e_.+$ 63 | REGEX_CONSTANT_NAME = ^co_.+$ 64 | REGEX_SUBTYPE_NAME = ^.+_type$ 65 | # Override defaults for TrivadisGuidelines3 validator 66 | cop.1050.threshold = 2 67 | cop.2185.threshold = 4 68 | cop.2410.boolean.strings = true,false,t,f,0,1,2,yes,no,y,n,ja,nein,j,si,s,oui,non,o,l_true,l_false,co_true,co_false,co_numeric_true,co_numeric_false 69 | cop.5050.threshold.from = 20000 70 | cop.5050.threshold.to = 20999 71 | cop.7210.threshold = 2000 72 | ``` 73 | 74 | If you are using the `TrivadisGuidelines3Plus` validator, the properties for the `TrivadisGuidelines3` validator can also be configured in this properties file. 75 | 76 | ### GLP 77 | 78 | This is a simple validator to check the following naming convention guidelines: 79 | 80 | Guideline | Message 81 | --------- | ----------- 82 | G-9001 | Always prefix global variables with 'g_'. 83 | G-9002 | Always prefix local variables with 'l_'. 84 | G-9003 | Always prefix parameters with 'p_'. 85 | 86 | This validator checks just these three guidelines. It does not extend the [Trivadis PL/SQL & SQL Coding Guidelines](https://trivadis.github.io/plsql-and-sql-coding-guidelines/). 87 | 88 | ### SQLInjection 89 | 90 | This validator implements the following guideline: 91 | 92 | Guideline | Message 93 | --------- | ----------- 94 | G-9501 | Never use parameter in string expression of dynamic SQL. Use asserted local variable instead. 95 | 96 | It looks for unasserted parameters used in [`EXECUTE IMMEDIATE`](https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/EXECUTE-IMMEDIATE-statement.html#GUID-C3245A95-B85B-4280-A01F-12307B108DC8) statements and [`OPEN FOR`](https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/OPEN-FOR-statement.html#GUID-EB7AF439-FDD3-4461-9E3F-B621E8ABFB96) statements. All parameters used in these statements must be asserted with one of the subprograms provided by [`DBMS_ASSERT`](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_ASSERT.html#GUID-27B4B484-7CD7-48FE-89A3-B630ADE1CB50). 97 | 98 | See [example](src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9501.sql) 99 | 100 | ### Hint 101 | 102 | This validator implements the following guidelines: 103 | 104 | Guideline | Message 105 | --------- | ----------- 106 | G-9600 | Never define more than one comment with hints. 107 | G-9601 | Never use unknown hints. 108 | G-9602 | Always use the alias name instead of the table name. 109 | G-9603 | Never reference an unknown table/alias. 110 | G-9604 | Never use an invalid stats method. 111 | G-9605 | Never use an invalid stats keyword. 112 | 113 | Only the first comment containing hints is considered by the optimizer, therefore all hints violating `G-9600` are treated as ordinary comments by the Oracle Database. 114 | 115 | Using unknown hints might invalidate all subsequent hints. This happens when you use for example `NOLOGGING`. That's expected and not a bug. See MOS note 285285.1 or bug 8432870 for details. So, do not ignore `G-9601` violations. 116 | 117 | There are various hints that reference a table. The validator checks if the reference is valid. If an alias is defined for a table, but the table name is used in the hint then a `G-9602` violation is reported. If the table reference in the hint is neither a table name nor an alias then a `G-9603` violation is thrown. These violations should not be ignored either. 118 | 119 | However, the vadiator ignores the optional query block in the hint and assumes that all table references belong to the current query block. As a result some false positives are possible. Furthermore references to a table in a query block (e.g. `emp@qb1`) are not checked. This might lead to some false negatives. 120 | 121 | The guidelines `G-9604` and `G-9605` check the validity of the `method` and `keyword` in the `table_stats` hint. 122 | 123 | ### OverrideTrivadisGuidelines 124 | 125 | This validator shows how existing guideline checks can be overridden. 126 | 127 | The following guideline is overriden: 128 | 129 | Guideline | Message 130 | --------- | ----------- 131 | [G-1050](https://trivadis.github.io/plsql-and-sql-coding-guidelines/v4.4/4-language-usage/1-general/g-1050/) | Avoid using literals in your code. 132 | 133 | Literals as part of a [Logger](https://github.com/OraOpenSource/Logger) package call are not reported (see also [issue 8](https://github.com/Trivadis/plsql-cop-validators/issues/8)). 134 | 135 | ### TrivadisGuidelines3Plus 136 | 137 | This validator combines the validators 138 | 139 | - [TrivadisPlsqlNaming](#trivadisplsqlnaming), 140 | - [SQLInjection](#sqlinjection), 141 | - [Hint](#hint) and 142 | - [OverrideTrivadisGuidelines](#overridetrivadisguidelines) 143 | 144 | This way you can deal with an unbound number of validators without comproming the maintainablity. 145 | 146 | ## Use in db* CODECOP 147 | 148 | 1. Download db* CODECOP Command Line 149 | 150 | Download db* CODECOP Command Line from [here](https://github.com/Trivadis/plsql-cop-cli/releases). 151 | 152 | 2. Install db* CODECOP Command Line 153 | 154 | - Uncompress the distributed db* CODECOP archive file (e.g. tvdcc-4.x.x.zip) into a folder of your choice (hereinafter referred to as `TVDCC_HOME`). I use `$HOME/tvdcc` for `TVDCC_HOME` on my MacBook Pro. 155 | 156 | - For Windows platforms only: Amend the settings for JAVA_HOME in the tvdcc.cmd file to meet your environment settings. Use at least a Java 8 runtime environment (JRE) or development kit (JDK). 157 | 158 | - Include `TVDCC_HOME` in your PATH environment variable for handy interactive usage. 159 | 160 | - Optionally copy your commercial license file into the `TVDCC_HOME` directory. For simplicity name the file tvdcc.lic. 161 | 162 | 3. Download Custom Validators 163 | 164 | Download `sonar-plsql-cop-custom-validators-plugin-4.x.x.jar` from [here](https://github.com/Trivadis/cop-validators/releases). 165 | 166 | 4. Install Custom Validators 167 | 168 | Copy the previously downloaded jar file into the `plugin` folder of your `TVDCC_HOME` folder. 169 | 170 | 5. Run db* CODECOP with a Custom Validator 171 | 172 | Open a terminal window, change to the `TVDCC_HOME` directory and run the following command to all files in `$HOME/github/utPLSQL/source` with the custom validator `com.trivadis.tvdcc.validators.TrivadisGuidelines3Plus`: 173 | 174 | ``` 175 | ./tvdcc.sh path=$HOME/github/utPLSQL/source validator=com.trivadis.tvdcc.validators.TrivadisGuidelines3Plus 176 | ``` 177 | 178 | The `tvdcc_report.html` file contain the results. Here's an excerpt: 179 | 180 | ![db* CODECOP Report - File Issues](./images/cop-file-issues.png) 181 | 182 | ## Use in db* CODECOP for SQL Developer 183 | 184 | 1. Install db* CODECOP Command Line 185 | 186 | As explained [above](README.md#use-in-db*-codecop). 187 | 188 | 2. Download db* CODECOP for SQL Developer 189 | 190 | Download db* CODECOP for SQL Developer from [here](https://www.salvis.com/blog/plsql-cop-for-sql-developer/). 191 | 192 | 3. Install db* CODECOP for SQL Developer 193 | 194 | - Start SQL Developer 195 | - Select `Check for Updates…` in the help menu. 196 | - Use the `Install From Local File(s)` option to install the previously downloaded `TVDCC_for_SQLDev-*.zip` file. 197 | - Restart SQL Developer 198 | 199 | 4. Configure Validator 200 | 201 | Configure the validator in SQL Developer as shown in the following screenshot: 202 | 203 | ![Preferences](./images/sqldev-preferences.png) 204 | 205 | 5. Check Code 206 | 207 | Open the code to be checked in an editor and select `Check` from the context menu. 208 | 209 | ![Check](./images/sqldev-check.png) 210 | 211 | The check result is shown by default at the bottom of your SQL Developer workspace. 212 | 213 | ![Check](./images/sqldev-check-result.png) 214 | 215 | 216 | ## Use in db* CODECOP for SonarQube 217 | 218 | 1. Install SonarQube 219 | 220 | [See documentation](https://docs.sonarqube.org/latest/setup/install-server/). 221 | 222 | 2. Install Standalone or Secondary Plugin 223 | 224 | [See documentation](https://github.com/Trivadis/plsql-cop-sonar/tree/main#installation). 225 | 226 | 3. Install Child Plugin (Custom Validator Plugin) 227 | 228 | Download the `sonar-plsql-cop-custom-validators-plugin-x.x.x.jar` from [releases](https://github.com/Trivadis/plsql-cop-validators/releases). Then copy it to the extensions/plugins folder of your [SonarQube](http://docs.sonarqube.org/display/SONAR/Installing+a+Plugin) installation and restart the SonarQube server. 229 | 230 | 4. Configure Validator Config Class 231 | 232 | Login as Administrator in SonarQube. Go to `Administration`. Select `General Settings` from `Configuration` and click on `db* CODECOP`. Type one of the following into the `Validator Config class` field: 233 | 234 | - com.trivadis.sonar.plugin.GLPValidatorConfig 235 | - com.trivadis.sonar.plugin.TrivadisGuidelines3PlusValidatorConfig 236 | 237 | 5. Restart SonarQube 238 | 239 | Select `System` in the `Administration` menu and click on `Restart Server`. 240 | 241 | **Important**: Starting with SonarQube 9.1, rules and quality profiles are cached to improve the startup time (see [SONAR-15237](https://sonarsource.atlassian.net/browse/SONAR-15237)). To apply changes to `Language Key` and `Validator Config class`, the PL/SQL specific plugins must be uninstalled and then reinstalled. Configuration changes are not affected by this process. 242 | 243 | 6. Run Analysis 244 | 245 | Start an analysis from the command line as follows (see [docs](https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/) for more information): 246 | 247 | ``` 248 | cd $HOME/github/trivadis/plsql-cop-validators/src/main/resources 249 | sonar-scanner -Dsonar.projectKey="plsql-cop-validators" 250 | ``` 251 | 252 | By default the source code in the current directory is analyzed. Here's the result: 253 | 254 | ![SonarQube Issues in Code](./images/sonarqube-issues-in-code.png) 255 | 256 | ## How to Build 257 | 258 | 1. Install db* CODECOP Command Line 259 | 260 | As explained [above](README.md#use-in-db*-codecop). 261 | 262 | 2. Install Maven 263 | 264 | [Download](https://maven.apache.org/download.cgi) and install Apache Maven 3.8.6 265 | 266 | 3. Clone the cop-validators repository 267 | 268 | Clone or download this repository. 269 | 270 | 3. Install the required db* CODECOP libraries 271 | 272 | These libraries are not available in public Maven repositories. However, they are part 273 | of the db* CODECOP Command Line installed in step 1. You need to install these libraries 274 | into your local Maven repository. 275 | 276 | Open a terminal window in the cop-validators root folder and run Run the following shell script: 277 | 278 | ./install_tvdcc_libs.sh 279 | 280 | The shell script expects to find the library `tvdcc.jar` in `$HOME/tvdcc`. If it is not there, pass the path to the directory as parameter to this script. For example 281 | 282 | ./install_tvdcc_libs.sh $HOME/tvdcc 283 | 284 | 4. Build validator jar file 285 | 286 | Open a terminal window in the cop-validators root folder and run the Maven build by the following command 287 | 288 | mvn clean package 289 | 290 | ## License 291 | 292 | The db* CODECOP Validators are licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License. You may obtain a copy of the License at https://creativecommons.org/licenses/by-nc-nd/3.0/. 293 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - main 5 | - feature/* 6 | - bugfix/* 7 | paths: 8 | include: 9 | - src 10 | - pom.xml 11 | - azure-pipelines.yml 12 | exclude: 13 | - README.md 14 | 15 | pool: 16 | vmImage: 'ubuntu-latest' 17 | 18 | variables: 19 | MAVEN_CACHE_FOLDER: $(Pipeline.Workspace)/.m2/repository 20 | MAVEN_OPTS: '-Dmaven.repo.local=$(MAVEN_CACHE_FOLDER)' 21 | Agent.Source.Git.ShallowFetchDepth: 1 22 | 23 | steps: 24 | - task: MavenAuthenticate@0 25 | displayName: 'Maven Authenticate' 26 | inputs: 27 | artifactsFeeds: plsqlcop 28 | 29 | - task: Cache@2 30 | inputs: 31 | key: 'maven | "$(Agent.OS)" | **/pom.xml' 32 | restoreKeys: | 33 | maven | "$(Agent.OS)" 34 | maven 35 | path: $(MAVEN_CACHE_FOLDER) 36 | displayName: Cache Maven local repo 37 | 38 | - task: Maven@3 39 | displayName: 'Maven com.trivadis.tvdcc' 40 | inputs: 41 | mavenPomFile: 'pom.xml' 42 | mavenOptions: '$(MAVEN_OPTS)' 43 | goals: 'clean deploy -U -Pdefault,CI' 44 | publishJUnitResults: true 45 | jdkVersion: 17 46 | -------------------------------------------------------------------------------- /images/cop-file-issues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/plsql-cop-validators/5521d056b3bfb297f4262bd3dcdef9a5acf68e06/images/cop-file-issues.png -------------------------------------------------------------------------------- /images/sonarqube-issues-in-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/plsql-cop-validators/5521d056b3bfb297f4262bd3dcdef9a5acf68e06/images/sonarqube-issues-in-code.png -------------------------------------------------------------------------------- /images/sqldev-check-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/plsql-cop-validators/5521d056b3bfb297f4262bd3dcdef9a5acf68e06/images/sqldev-check-result.png -------------------------------------------------------------------------------- /images/sqldev-check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/plsql-cop-validators/5521d056b3bfb297f4262bd3dcdef9a5acf68e06/images/sqldev-check.png -------------------------------------------------------------------------------- /images/sqldev-preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/plsql-cop-validators/5521d056b3bfb297f4262bd3dcdef9a5acf68e06/images/sqldev-preferences.png -------------------------------------------------------------------------------- /install_tvdcc_libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # set the directory where libraries of db* CODECOP CLI are stored 4 | if [[ "$1" = "" ]]; then 5 | TVDCC_DIR="$HOME/tvdcc" 6 | else 7 | TVDCC_DIR="$1" 8 | fi 9 | 10 | # check db* CODECOP CLI root directory 11 | if ! test -f "${TVDCC_DIR}/tvdcc.jar"; then 12 | echo "Error: ${TVDCC_DIR} is not a valid path to the root path of db* CODECOP CLI." 13 | echo " Please pass a valid path to this script." 14 | exit 1 15 | fi 16 | 17 | # define versions according usage in pom.xml 18 | TVDCC_VERSION="5.0.1" 19 | PLSQL_VERSION="5.0.1" 20 | 21 | # install JAR files into local Maven repository, these libs are not available in public Maven repositories 22 | mvn install:install-file -Dfile=$TVDCC_DIR/tvdcc.jar -DgeneratePom=true \ 23 | -DgroupId=trivadis.tvdcc -DartifactId=tvdcc -Dversion=$TVDCC_VERSION -Dpackaging=jar 24 | mvn install:install-file -Dfile=$TVDCC_DIR/lib/plsql-${PLSQL_VERSION}.jar -DgeneratePom=true \ 25 | -DgroupId=trivadis.oracle -DartifactId=plsql -Dversion=$PLSQL_VERSION -Dpackaging=jar 26 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | trivadis.tvdcc 7 | sonar-plsql-cop-custom-validators-plugin 8 | 5.0.2-SNAPSHOT 9 | sonar-plugin 10 | 11 | UTF-8 12 | 8 13 | 17 14 | 2.28.0 15 | false 16 | false 17 | 18 | 19 | 20 | default 21 | 22 | true 23 | 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-compiler-plugin 29 | 3.12.1 30 | 31 | 32 | 33 | ${jdk.version} 34 | ${jdk.test.version} 35 | 36 | 37 | 38 | 39 | 40 | 41 | idea 42 | 43 | false 44 | 45 | idea.maven.embedder.version 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-compiler-plugin 53 | 3.12.1 54 | 55 | 56 | 57 | ${jdk.test.version} 58 | ${jdk.test.version} 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | false 67 | 68 | CI 69 | 70 | 71 | plsqlcop 72 | https://pkgs.dev.azure.com/tvdvsts/2001.5038513/_packaging/plsqlcop/maven/v1 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | 81 | 82 | 83 | plsqlcop 84 | https://pkgs.dev.azure.com/tvdvsts/2001.5038513/_packaging/plsqlcop/maven/v1 85 | 86 | true 87 | 88 | 89 | true 90 | 91 | 92 | 93 | 94 | 95 | plsqlcop 96 | https://pkgs.dev.azure.com/tvdvsts/2001.5038513/_packaging/plsqlcop/maven/v1 97 | 98 | 99 | plsqlcop 100 | https://pkgs.dev.azure.com/tvdvsts/2001.5038513/_packaging/plsqlcop/maven/v1 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | trivadis.tvdcc 109 | tvdcc 110 | 5.0.1 111 | provided 112 | 113 | 114 | 115 | trivadis.oracle 116 | plsql 117 | 5.0.1 118 | provided 119 | 120 | 121 | 122 | org.sonarsource.sonarqube 123 | sonar-plugin-api 124 | 7.9 125 | provided 126 | 127 | 128 | 129 | 130 | org.eclipse.xtext 131 | org.eclipse.xtext 132 | ${xtext.version} 133 | provided 134 | 135 | 136 | org.eclipse.xtext 137 | org.eclipse.xtext.testing 138 | ${xtext.version} 139 | test 140 | 141 | 142 | org.eclipse.xtext 143 | org.eclipse.xtext.common.types 144 | ${xtext.version} 145 | test 146 | 147 | 148 | junit 149 | junit 150 | 4.13.2 151 | test 152 | 153 | 154 | 155 | 156 | 157 | ${project.basedir}/src/main/java 158 | 159 | 160 | src/main/resources 161 | 162 | **/*.* 163 | 164 | 165 | 166 | 167 | 168 | org.sonarsource.sonar-packaging-maven-plugin 169 | sonar-packaging-maven-plugin 170 | 1.21.0.505 171 | true 172 | 173 | plsql-cop-custom-validators 174 | com.trivadis.sonar.plugin.PlsqlCustomPlugin 175 | plsqlcop 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | 3.12.1 181 | maven-compiler-plugin 182 | 183 | ${jdk.version} 184 | ${jdk.version} 185 | 186 | **/*.java 187 | 188 | 189 | 190 | 191 | org.apache.maven.plugins 192 | maven-deploy-plugin 193 | 3.1.1 194 | 195 | 196 | org.codehaus.mojo 197 | exec-maven-plugin 198 | 3.1.1 199 | 200 | 201 | 202 | glp 203 | compile 204 | 205 | java 206 | 207 | 208 | com.trivadis.tvdcc.generators.GLPGenmodel 209 | test 210 | false 211 | ${skip.genmodel} 212 | 213 | 214 | 215 | guidelines3plus 216 | compile 217 | 218 | java 219 | 220 | 221 | com.trivadis.tvdcc.generators.TrivadisGuidelines3PlusGenmodel 222 | test 223 | false 224 | ${skip.genmodel} 225 | 226 | 227 | 228 | 229 | 230 | org.apache.maven.plugins 231 | maven-surefire-plugin 232 | 3.2.5 233 | 234 | 235 | default-test 236 | 237 | test 238 | 239 | 240 | ${skipTests} 241 | 1 242 | true 243 | 244 | **/*.java 245 | 246 | 247 | **/TrivadisPlsqlNamingTest.java 248 | **/TrivadisPlsqlNamingPropertiesFileTest.java 249 | **/TrivadisPlsqlNamingSystemPropertiesTest.java 250 | **/GLPTest.java 251 | **/SQLInjectionTest.java 252 | **/OverrideTrivadisGuidelinesTest.java 253 | **/TrivadisGuidelines3PlusTest.java 254 | **/HintTest.java 255 | 256 | 257 | 258 | 259 | trivadis-plsql-naming-test 260 | 261 | test 262 | 263 | 264 | ${skipTests} 265 | 1 266 | true 267 | 268 | **/TrivadisPlsqlNamingTest.java 269 | 270 | 271 | 272 | 273 | trivadis-plsql-naming-properties-file-test 274 | 275 | test 276 | 277 | 278 | ${skipTests} 279 | 1 280 | true 281 | 282 | **/TrivadisPlsqlNamingPropertiesFileTest.java 283 | 284 | 285 | 286 | 287 | trivadis-plsql-naming-system-properties-test 288 | 289 | test 290 | 291 | 292 | ${skipTests} 293 | 1 294 | true 295 | 296 | **/TrivadisPlsqlNamingSystemPropertiesTest.java 297 | 298 | 299 | 300 | 301 | glp-test 302 | 303 | test 304 | 305 | 306 | ${skipTests} 307 | 1 308 | true 309 | 310 | **/GLPTest.java 311 | 312 | 313 | 314 | 315 | sqlinjection-test 316 | 317 | test 318 | 319 | 320 | ${skipTests} 321 | 1 322 | true 323 | 324 | **/SQLInjectionTest.java 325 | 326 | 327 | 328 | 329 | override-trivadis-guidelines-test 330 | 331 | test 332 | 333 | 334 | ${skipTests} 335 | 1 336 | true 337 | 338 | **/OverrideTrivadisGuidelinesTest.java 339 | 340 | 341 | 342 | 343 | trivadis-guidelines-3plus-test 344 | 345 | test 346 | 347 | 348 | ${skipTests} 349 | 1 350 | true 351 | 352 | **/TrivadisGuidelines3PlusTest.java 353 | 354 | 355 | 356 | 357 | hint-test 358 | 359 | test 360 | 361 | 362 | ${skipTests} 363 | 1 364 | true 365 | 366 | **/HintTest.java 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | org.eclipse.m2e 378 | lifecycle-mapping 379 | 1.0.0 380 | 381 | 382 | 383 | 384 | 385 | 386 | org.codehaus.mojo 387 | 388 | 389 | exec-maven-plugin 390 | 391 | 392 | [3.0.0,) 393 | 394 | 395 | java 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | db* CODECOP for SonarQube (Child) 412 | Custom validators to analyze PL/SQL and SQL code. Extends db* CODECOP for SonarQube (Standalone/Secondary), db* CODECOP CLI and db* CODECOP for SQL Developer. 413 | https://github.com/Trivadis/plsql-cop-validators 414 | 2017 415 | 416 | 417 | CC BY-NC-ND 3.0 418 | https://creativecommons.org/licenses/by-nc-nd/3.0/ 419 | manual 420 | 421 | 422 | 423 | Trivadis - Part of Accenture 424 | https://www.accenture.com/ 425 | 426 | 427 | 428 | philipp.salvisberg@accenture.com 429 | Philipp Salvisberg 430 | https://github.com/PhilippSalvisberg/ 431 | PhilippSalvisberg 432 | 433 | lead 434 | developer 435 | 436 | 437 | 438 | 439 | scm:git:https://github.com/Trivadis/plsql-cop-validators.git 440 | https://github.com/Trivadis/plsql-cop-validators 441 | 442 | 443 | GitHub 444 | https://github.com/Trivadis/plsql-cop-validators/issues 445 | 446 | 447 | 448 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/sonar/plugin/GLPValidatorConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.trivadis.sonar.plugin; 18 | 19 | import com.trivadis.tvdcc.validators.GLP; 20 | 21 | public class GLPValidatorConfig implements ValidatorConfig { 22 | 23 | @Override 24 | public String getModelResourcePath() { 25 | return "/GLP/genmodel/plsqlcop-model.xml"; 26 | } 27 | 28 | @Override 29 | public String getRulesResourcePath() { 30 | return "/GLP/genmodel/rules.xml"; 31 | } 32 | 33 | @Override 34 | public Class getValidatorClass() { 35 | return GLP.class; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/sonar/plugin/PlsqlCustomPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.trivadis.sonar.plugin; 18 | 19 | import org.sonar.api.Plugin; 20 | 21 | public class PlsqlCustomPlugin implements Plugin { 22 | public PlsqlCustomPlugin() { 23 | } 24 | 25 | @Override 26 | public void define(Context context) { 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/sonar/plugin/TrivadisGuidelines3PlusValidatorConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.trivadis.sonar.plugin; 18 | 19 | import com.trivadis.tvdcc.validators.TrivadisGuidelines3Plus; 20 | 21 | public class TrivadisGuidelines3PlusValidatorConfig implements ValidatorConfig { 22 | 23 | @Override 24 | public String getModelResourcePath() { 25 | return "/TrivadisGuidelines3Plus/genmodel/plsqlcop-model.xml"; 26 | } 27 | 28 | @Override 29 | public String getRulesResourcePath() { 30 | return "/TrivadisGuidelines3Plus/genmodel/rules.xml"; 31 | } 32 | 33 | @Override 34 | public Class getValidatorClass() { 35 | return TrivadisGuidelines3Plus.class; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/generators/GLPGenmodel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.trivadis.tvdcc.generators; 18 | 19 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 20 | import com.trivadis.tvdcc.genmodel.GenRulesXml; 21 | import com.trivadis.tvdcc.genmodel.GenSqaleXml; 22 | import com.trivadis.tvdcc.validators.GLP; 23 | 24 | public class GLPGenmodel { 25 | 26 | public static void genPlsqlcopModelXml() { 27 | GenSqaleXml gen = new GenSqaleXml(); 28 | gen.generate("./src/main/resources/GLP"); 29 | } 30 | 31 | public static void genRulesXml() { 32 | GenRulesXml gen = new GenRulesXml(); 33 | String targetDir = "./src/main/resources/GLP"; 34 | gen.generate(targetDir, "./src/main/resources/GLP/sample"); 35 | } 36 | 37 | public static void main(String[] args) { 38 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(GLP.class); 39 | genPlsqlcopModelXml(); 40 | genRulesXml(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/generators/GenUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.trivadis.tvdcc.generators; 18 | 19 | import java.io.IOException; 20 | import java.nio.file.FileVisitOption; 21 | import java.nio.file.Files; 22 | import java.nio.file.Path; 23 | import java.nio.file.Paths; 24 | import java.util.List; 25 | import java.util.stream.Collectors; 26 | 27 | 28 | public class GenUtil { 29 | public static String getPath(String dirName) throws IOException { 30 | String ret = null; 31 | List dirs = Files.walk(Paths.get(""), FileVisitOption.FOLLOW_LINKS).filter(f -> f.toFile().getAbsolutePath().contains(dirName.substring(2))) 32 | .collect(Collectors.toList()); 33 | if (dirs.size() > 0) { 34 | ret = dirs.get(0).toFile().getAbsolutePath(); 35 | } 36 | return ret; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/generators/TrivadisGuidelines3PlusGenmodel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.trivadis.tvdcc.generators; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.URISyntaxException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import java.nio.file.StandardCopyOption; 27 | import java.util.Enumeration; 28 | import java.util.jar.JarEntry; 29 | import java.util.jar.JarFile; 30 | 31 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 32 | import com.trivadis.tvdcc.genmodel.GenRulesXml; 33 | import com.trivadis.tvdcc.genmodel.GenSqaleXml; 34 | import com.trivadis.tvdcc.validators.TrivadisGuidelines3; 35 | import com.trivadis.tvdcc.validators.TrivadisGuidelines3Plus; 36 | 37 | public class TrivadisGuidelines3PlusGenmodel { 38 | 39 | public static void genPlsqlcopModelXml() { 40 | GenSqaleXml gen = new GenSqaleXml(); 41 | gen.generate("./src/main/resources/TrivadisGuidelines3Plus"); 42 | } 43 | 44 | public static void copy(String sourceDir, String targetDir) throws IOException { 45 | Files.walk(Paths.get(sourceDir)).forEach(sourceFile -> { 46 | Path targetFile = Paths.get(targetDir, sourceFile.toString().substring(sourceDir.length())); 47 | try { 48 | if (sourceFile.toFile().isFile()) { 49 | Files.copy(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING); 50 | } 51 | } catch (IOException e) { 52 | throw new RuntimeException(e); 53 | } 54 | }); 55 | } 56 | 57 | public static void copyGuidelinesFromJar(String targetDir) throws IOException, URISyntaxException { 58 | File tvdccJarFile = new File( 59 | TrivadisGuidelines3.class.getProtectionDomain().getCodeSource().getLocation().toURI()); 60 | JarFile jarFile = new JarFile(tvdccJarFile); 61 | Enumeration entries = jarFile.entries(); 62 | while (entries.hasMoreElements()) { 63 | JarEntry entry = entries.nextElement(); 64 | if (!entry.isDirectory() && entry.getName().startsWith("guidelines/")) { 65 | InputStream entryInputStream = jarFile.getInputStream(entry); 66 | Path targetFile = Paths.get(targetDir, entry.getName().substring(10)); 67 | Files.copy(entryInputStream, targetFile, StandardCopyOption.REPLACE_EXISTING); 68 | } 69 | } 70 | jarFile.close(); 71 | } 72 | 73 | public static void genRulesXml() throws URISyntaxException, IOException { 74 | GenRulesXml gen = new GenRulesXml(); 75 | String tempDir = Files.createTempDirectory("genmodel_").toString(); 76 | copyGuidelinesFromJar(tempDir); 77 | copy("./src/main/resources/TrivadisGuidelines3Plus/sample", tempDir); 78 | gen.generate("./src/main/resources/TrivadisGuidelines3Plus", tempDir.toString()); 79 | } 80 | 81 | public static void main(String[] args) throws URISyntaxException, IOException { 82 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(TrivadisGuidelines3Plus.class); 83 | genPlsqlcopModelXml(); 84 | genRulesXml(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/validators/GLP.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators; 17 | 18 | import com.trivadis.oracle.plsql.plsql.CreatePackage; 19 | import com.trivadis.oracle.plsql.plsql.CreatePackageBody; 20 | import com.trivadis.oracle.plsql.plsql.ParameterDeclaration; 21 | import com.trivadis.oracle.plsql.plsql.VariableDeclaration; 22 | import com.trivadis.oracle.plsql.validation.PLSQLCopGuideline; 23 | import com.trivadis.oracle.plsql.validation.PLSQLCopValidator; 24 | import com.trivadis.oracle.plsql.validation.PLSQLValidator; 25 | import com.trivadis.oracle.plsql.validation.Remediation; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import org.eclipse.emf.ecore.EObject; 29 | import org.eclipse.emf.ecore.EPackage; 30 | import org.eclipse.xtext.validation.Check; 31 | import org.eclipse.xtext.validation.EValidatorRegistrar; 32 | 33 | public class GLP extends PLSQLValidator implements PLSQLCopValidator { 34 | private HashMap guidelines; 35 | 36 | // must be overridden to avoid duplicate issues when used via ComposedChecks 37 | @Override 38 | public void register(EValidatorRegistrar registrar) { 39 | final List ePackages = getEPackages(); 40 | if (registrar.getRegistry().get(ePackages.get(0)) == null) { 41 | // standalone validator, default registration required 42 | super.register(registrar); 43 | } 44 | } 45 | 46 | @Override 47 | public HashMap getGuidelines() { 48 | if (guidelines == null) { 49 | guidelines = new HashMap<>(); 50 | // register parent guidelines 51 | for (final Integer k : super.getGuidelines().keySet()) { 52 | guidelines.put(k, super.getGuidelines().get(k)); 53 | } 54 | // register guidelines 55 | guidelines.put(9001, new PLSQLCopGuideline(9001, "Always prefix global variables with 'g_'.", MINOR, 56 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 57 | guidelines.put(9002, new PLSQLCopGuideline(9002, "Always prefix local variables with 'l_'.", MINOR, 58 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 59 | guidelines.put(9003, new PLSQLCopGuideline(9003, "Always prefix parameters with 'p_'.", MINOR, 60 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 61 | } 62 | return guidelines; 63 | } 64 | 65 | @Check 66 | public void checkVariableName(VariableDeclaration v) { 67 | final EObject parent = v.eContainer().eContainer(); 68 | final String name = v.getVariable().getValue().toLowerCase(); 69 | if (parent instanceof CreatePackage || parent instanceof CreatePackageBody) { 70 | if (!name.startsWith("g_")) { 71 | warning(9001, v.getVariable(), v); 72 | } 73 | } else { 74 | if (!name.startsWith("l_")) { 75 | warning(9002, v.getVariable(), v); 76 | } 77 | } 78 | } 79 | 80 | @Check 81 | public void checkParameterName(ParameterDeclaration p) { 82 | if (!p.getParameter().getValue().toLowerCase().startsWith("p_")) { 83 | warning(9003, p.getParameter(), p); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/validators/OverrideTrivadisGuidelines.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators; 17 | 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | 22 | import org.eclipse.emf.ecore.EObject; 23 | import org.eclipse.emf.ecore.EPackage; 24 | import org.eclipse.xtext.EcoreUtil2; 25 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; 26 | import org.eclipse.xtext.validation.Check; 27 | import org.eclipse.xtext.validation.EValidatorRegistrar; 28 | 29 | import com.trivadis.oracle.plsql.plsql.BinaryCompoundExpressionLevel6; 30 | import com.trivadis.oracle.plsql.plsql.BinaryCompoundExpressionLevel7; 31 | import com.trivadis.oracle.plsql.plsql.CollectionTypeDefinition; 32 | import com.trivadis.oracle.plsql.plsql.ConstantDeclaration; 33 | import com.trivadis.oracle.plsql.plsql.PLSQLFile; 34 | import com.trivadis.oracle.plsql.plsql.SimpleExpressionNameValue; 35 | import com.trivadis.oracle.plsql.plsql.SimpleExpressionNumberValue; 36 | import com.trivadis.oracle.plsql.plsql.SimpleExpressionStringValue; 37 | import com.trivadis.oracle.plsql.plsql.UserDefinedType; 38 | import com.trivadis.oracle.plsql.validation.PLSQLCopValidator; 39 | 40 | public class OverrideTrivadisGuidelines extends TrivadisGuidelines3 implements PLSQLCopValidator { 41 | 42 | // must be overridden to avoid duplicate issues when used via ComposedChecks 43 | @Override 44 | public void register(EValidatorRegistrar registrar) { 45 | List ePackages = getEPackages(); 46 | if (registrar.getRegistry().get(ePackages.get(0)) == null) { 47 | // standalone validator, default registration required 48 | super.register(registrar); 49 | } 50 | } 51 | 52 | public boolean isConstantDeclaration( 53 | 54 | final EObject obj) { 55 | ConstantDeclaration constantDeclaration = EcoreUtil2.getContainerOfType(obj, ConstantDeclaration.class); 56 | return (constantDeclaration != null); 57 | } 58 | 59 | public boolean isLoggerCall(EObject obj) { 60 | if (obj == null) { 61 | return false; 62 | } else { 63 | BinaryCompoundExpressionLevel7 func = EcoreUtil2.getContainerOfType(obj, 64 | BinaryCompoundExpressionLevel7.class); 65 | if (func == null) { 66 | return false; 67 | } else { 68 | if (func.getLeft() instanceof SimpleExpressionNameValue) { 69 | if (func.eContainer() instanceof BinaryCompoundExpressionLevel6) { 70 | BinaryCompoundExpressionLevel6 pkg = (BinaryCompoundExpressionLevel6) func.eContainer(); 71 | if (pkg.getBinaryOperator().equals(".")) { 72 | if (pkg.getLeft() instanceof SimpleExpressionNameValue) { 73 | SimpleExpressionNameValue pkgName = (SimpleExpressionNameValue) pkg.getLeft(); 74 | if (pkgName.getValue().equalsIgnoreCase("logger")) { 75 | return true; 76 | } 77 | } 78 | } 79 | } 80 | } 81 | return isLoggerCall(func.eContainer()); 82 | } 83 | } 84 | } 85 | 86 | /* 87 | * Override G-1050: Avoid using literals in your code. (guidelines version 3.x) 88 | * Override G-05: Avoid using literals in your code. (guidelines version 2.x) 89 | * re-implement existing functionality while ignoring logger calls 90 | * "&& !isLoggerCall(obj)" is the only addition to the original code 91 | */ 92 | @Check 93 | @Override 94 | public void checkGuideline5(PLSQLFile file) { 95 | HashMap map = new HashMap<>(); 96 | int threshold = Integer.parseInt(System.getProperty(COP_1050_THRESHOLD, "2")); 97 | List warnings = new ArrayList<>(); 98 | List numbers = EcoreUtil2.getAllContentsOfType(file, 99 | SimpleExpressionNumberValue.class); 100 | for (SimpleExpressionNumberValue literal : numbers) { 101 | ConstantDeclaration declartion = EcoreUtil2.getContainerOfType(literal, ConstantDeclaration.class); 102 | UserDefinedType udf = EcoreUtil2.getContainerOfType(literal, UserDefinedType.class); 103 | CollectionTypeDefinition collDef = EcoreUtil2.getContainerOfType(literal, CollectionTypeDefinition.class); 104 | if (declartion == null && udf == null && collDef == null && !literal.getValue().replace(".", "").equals("0") 105 | && !literal.getValue().replace(".", "").equals("1")) { 106 | updateLiteralMap(literal, map, threshold); 107 | warnings.add(literal); 108 | } 109 | } 110 | List strings = EcoreUtil2.getAllContentsOfType(file, 111 | SimpleExpressionStringValue.class); 112 | for (SimpleExpressionStringValue literal : strings) { 113 | ConstantDeclaration declartion = EcoreUtil2.getContainerOfType(literal, ConstantDeclaration.class); 114 | if (declartion == null) { 115 | UserDefinedType udf = EcoreUtil2.getContainerOfType(literal, UserDefinedType.class); 116 | if (udf == null) { 117 | updateLiteralMap(literal, map, threshold); 118 | warnings.add(literal); 119 | } 120 | } 121 | } 122 | for (EObject obj : warnings) { 123 | if (!withinThreshold(obj, map, threshold) && !isLoggerCall(obj)) { 124 | warning(getGuidelineMsg(5), obj, null, getGuidelineId(5), serialize(NodeModelUtils.getNode(obj))); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/validators/SQLInjection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators; 17 | 18 | import com.trivadis.oracle.plsql.plsql.BinaryCompoundExpressionLevel6; 19 | import com.trivadis.oracle.plsql.plsql.BinaryCompoundExpressionLevel7; 20 | import com.trivadis.oracle.plsql.plsql.Body; 21 | import com.trivadis.oracle.plsql.plsql.Condition; 22 | import com.trivadis.oracle.plsql.plsql.ConstantDeclaration; 23 | import com.trivadis.oracle.plsql.plsql.ConstructorDeclaration; 24 | import com.trivadis.oracle.plsql.plsql.CreateFunction; 25 | import com.trivadis.oracle.plsql.plsql.CreateProcedure; 26 | import com.trivadis.oracle.plsql.plsql.DeclareSection; 27 | import com.trivadis.oracle.plsql.plsql.ElementType; 28 | import com.trivadis.oracle.plsql.plsql.ExecuteImmediateStatement; 29 | import com.trivadis.oracle.plsql.plsql.Expression; 30 | import com.trivadis.oracle.plsql.plsql.FuncDeclInType; 31 | import com.trivadis.oracle.plsql.plsql.FunctionDefinition; 32 | import com.trivadis.oracle.plsql.plsql.FunctionOrParenthesisParameter; 33 | import com.trivadis.oracle.plsql.plsql.OpenForStatement; 34 | import com.trivadis.oracle.plsql.plsql.ParameterDeclaration; 35 | import com.trivadis.oracle.plsql.plsql.PlsqlBlock; 36 | import com.trivadis.oracle.plsql.plsql.ProcDeclInType; 37 | import com.trivadis.oracle.plsql.plsql.ProcedureCallOrAssignmentStatement; 38 | import com.trivadis.oracle.plsql.plsql.ProcedureDefinition; 39 | import com.trivadis.oracle.plsql.plsql.QualifiedSqlNameExpression; 40 | import com.trivadis.oracle.plsql.plsql.SimpleExpressionNameValue; 41 | import com.trivadis.oracle.plsql.plsql.UserDefinedType; 42 | import com.trivadis.oracle.plsql.plsql.VariableDeclaration; 43 | import com.trivadis.oracle.plsql.validation.PLSQLCopGuideline; 44 | import com.trivadis.oracle.plsql.validation.PLSQLCopValidator; 45 | import com.trivadis.oracle.plsql.validation.PLSQLValidator; 46 | import com.trivadis.oracle.plsql.validation.Remediation; 47 | 48 | import java.util.*; 49 | import java.util.stream.Collectors; 50 | 51 | import org.eclipse.emf.ecore.EObject; 52 | import org.eclipse.emf.ecore.EPackage; 53 | import org.eclipse.xtext.EcoreUtil2; 54 | import org.eclipse.xtext.nodemodel.ICompositeNode; 55 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; 56 | import org.eclipse.xtext.validation.Check; 57 | import org.eclipse.xtext.validation.EValidatorRegistrar; 58 | import org.eclipse.xtext.xbase.lib.CollectionLiterals; 59 | 60 | public class SQLInjection extends PLSQLValidator implements PLSQLCopValidator { 61 | private HashMap guidelines; 62 | private final List ASSERT_PACKAGES = Collections 63 | .unmodifiableList(CollectionLiterals.newArrayList("dbms_assert", "ut_utils")); 64 | 65 | // must be overridden to avoid duplicate issues when used via ComposedChecks 66 | @Override 67 | public void register(EValidatorRegistrar registrar) { 68 | final List ePackages = getEPackages(); 69 | if (registrar.getRegistry().get(ePackages.get(0)) == null) { 70 | // standalone validator, default registration required 71 | super.register(registrar); 72 | } 73 | } 74 | 75 | @Override 76 | public HashMap getGuidelines() { 77 | if (guidelines == null) { 78 | guidelines = new HashMap<>(); 79 | guidelines.put(9501, new PLSQLCopGuideline(9501, 80 | "Never use parameter in string expression of dynamic SQL. Use asserted local variable instead.", 81 | BLOCKER, SECURITY_FEATURES, Remediation.createConstantPerIssue(5))); 82 | } 83 | return guidelines; 84 | } 85 | 86 | public boolean isStringParameter(ParameterDeclaration param) { 87 | ElementType etype = param.getType(); 88 | if (etype instanceof UserDefinedType) { 89 | QualifiedSqlNameExpression udf = ((UserDefinedType) etype).getUserDefinedType(); 90 | ICompositeNode node = NodeModelUtils.getNode(udf); 91 | String nodeText = node.getText(); 92 | return nodeText.toLowerCase().contains("char"); 93 | } 94 | return false; 95 | } 96 | 97 | public boolean isParameter(CreateProcedure proc, SimpleExpressionNameValue n) { 98 | for (final ParameterDeclaration p : proc.getParams()) { 99 | if (p.getParameter().getValue().equalsIgnoreCase(n.getValue())) { 100 | return isStringParameter(p); 101 | } 102 | } 103 | return false; 104 | } 105 | 106 | public boolean isParameter(CreateFunction func, SimpleExpressionNameValue n) { 107 | for (final ParameterDeclaration f : func.getParams()) { 108 | if (f.getParameter().getValue().equalsIgnoreCase(n.getValue())) { 109 | return isStringParameter(f); 110 | } 111 | } 112 | return false; 113 | } 114 | 115 | public boolean isParameter(ProcedureDefinition proc, SimpleExpressionNameValue n) { 116 | for (final ParameterDeclaration p : proc.getHeading().getParams()) { 117 | if (p.getParameter().getValue().equalsIgnoreCase(n.getValue())) { 118 | return isStringParameter(p); 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | public boolean isParameter(FunctionDefinition func, SimpleExpressionNameValue n) { 125 | for (final ParameterDeclaration f : func.getHeading().getParams()) { 126 | if (f.getParameter().getValue().equalsIgnoreCase(n.getValue())) { 127 | return isStringParameter(f); 128 | } 129 | } 130 | return false; 131 | } 132 | 133 | public boolean isParameter(ProcDeclInType proc, SimpleExpressionNameValue n) { 134 | for (final ParameterDeclaration p : proc.getParams()) { 135 | if (p.getParameter().getValue().equalsIgnoreCase(n.getValue())) { 136 | return isStringParameter(p); 137 | } 138 | } 139 | return false; 140 | } 141 | 142 | public boolean isParameter(FuncDeclInType func, SimpleExpressionNameValue n) { 143 | for (final ParameterDeclaration f : func.getParams()) { 144 | if (f.getParameter().getValue().equalsIgnoreCase(n.getValue())) { 145 | return isStringParameter(f); 146 | } 147 | } 148 | return false; 149 | } 150 | 151 | public boolean isParameter(ConstructorDeclaration func, SimpleExpressionNameValue n) { 152 | for (final ParameterDeclaration f : func.getParams()) { 153 | if (f.getParameter().getValue().equalsIgnoreCase(n.getValue())) { 154 | return isStringParameter(f); 155 | } 156 | } 157 | return false; 158 | } 159 | 160 | public boolean isParameter(SimpleExpressionNameValue n) { 161 | final CreateProcedure parentProcedure = EcoreUtil2.getContainerOfType(n, CreateProcedure.class); 162 | if (parentProcedure != null) { 163 | return isParameter(parentProcedure, n); 164 | } else { 165 | final CreateFunction parentFunction = EcoreUtil2.getContainerOfType(n, CreateFunction.class); 166 | if (parentFunction != null) { 167 | return isParameter(parentFunction, n); 168 | } else { 169 | final ProcedureDefinition parentPackageProcedure = EcoreUtil2.getContainerOfType(n, 170 | ProcedureDefinition.class); 171 | if (parentPackageProcedure != null) { 172 | return isParameter(parentPackageProcedure, n); 173 | } else { 174 | final FunctionDefinition parentPackageFunction = EcoreUtil2.getContainerOfType(n, 175 | FunctionDefinition.class); 176 | if (parentPackageFunction != null) { 177 | return isParameter(parentPackageFunction, n); 178 | } else { 179 | final ProcDeclInType parentTypeProcedure = EcoreUtil2.getContainerOfType(n, 180 | ProcDeclInType.class); 181 | if (parentTypeProcedure != null) { 182 | return isParameter(parentTypeProcedure, n); 183 | } else { 184 | final FuncDeclInType parentTypeFunction = EcoreUtil2.getContainerOfType(n, 185 | FuncDeclInType.class); 186 | if (parentTypeFunction != null) { 187 | return isParameter(parentTypeFunction, n); 188 | } else { 189 | final ConstructorDeclaration parentTypeConstructor = EcoreUtil2.getContainerOfType(n, 190 | ConstructorDeclaration.class); 191 | if (parentTypeConstructor != null) { 192 | return isParameter(parentTypeConstructor, n); 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } 199 | } 200 | return false; 201 | } 202 | 203 | public String getQualifiedFunctionName(EObject obj) { 204 | final BinaryCompoundExpressionLevel7 expr7 = EcoreUtil2.getContainerOfType(obj, 205 | BinaryCompoundExpressionLevel7.class); 206 | if (expr7 != null) { 207 | final Expression left = expr7.getLeft(); 208 | if (left instanceof SimpleExpressionNameValue) { 209 | final String functionName = ((SimpleExpressionNameValue) left).getValue(); 210 | final BinaryCompoundExpressionLevel6 parent = EcoreUtil2.getContainerOfType(expr7, 211 | BinaryCompoundExpressionLevel6.class); 212 | if (parent != null) { 213 | final Expression parentLeft = parent.getLeft(); 214 | if (parentLeft instanceof BinaryCompoundExpressionLevel6) { 215 | final Expression parentLeftLeft = ((BinaryCompoundExpressionLevel6) parentLeft).getLeft(); 216 | if (parentLeftLeft instanceof SimpleExpressionNameValue) { 217 | final String schemaName = ((SimpleExpressionNameValue) parentLeftLeft).getValue(); 218 | final Expression parentLeftRight = ((BinaryCompoundExpressionLevel6) parentLeft).getRight(); 219 | if (parentLeftRight instanceof SimpleExpressionNameValue) { 220 | final String packageName = ((SimpleExpressionNameValue) parentLeftRight).getValue(); 221 | return schemaName + "." + packageName + "." + functionName; 222 | } 223 | } 224 | } else if (parentLeft instanceof SimpleExpressionNameValue) { 225 | final String packageName = ((SimpleExpressionNameValue) parentLeft).getValue(); 226 | return packageName + "." + functionName; 227 | } 228 | } 229 | } 230 | } 231 | return ""; 232 | } 233 | 234 | public boolean contains(EObject obj, final String name) { 235 | final List names = EcoreUtil2.getAllContentsOfType(obj, 236 | SimpleExpressionNameValue.class); 237 | for (final SimpleExpressionNameValue n : names) { 238 | if (n.getValue().equalsIgnoreCase(name)) { 239 | return true; 240 | } 241 | } 242 | return false; 243 | } 244 | 245 | public ArrayList getItemsWithDefaults(SimpleExpressionNameValue n) { 246 | Body body = getBody(n); 247 | DeclareSection decl; 248 | if (body == null) { 249 | decl = EcoreUtil2.getContainerOfType(n, DeclareSection.class); 250 | } else { 251 | decl = getDeclareSection(body); 252 | } 253 | final ArrayList items = new ArrayList<>(); 254 | final List variables = EcoreUtil2.getAllContentsOfType(decl, VariableDeclaration.class); 255 | for (final VariableDeclaration v : variables) { 256 | if (v.getDefault() != null) { 257 | if (contains(v.getDefault(), n.getValue())) { 258 | items.add(v.getVariable()); 259 | } 260 | } 261 | } 262 | final List constants = EcoreUtil2.getAllContentsOfType(decl, ConstantDeclaration.class); 263 | for (final ConstantDeclaration c : constants) { 264 | if (c.getDefault() != null) { 265 | if (contains(c.getDefault(), n.getValue())) { 266 | items.add(c.getConstant()); 267 | } 268 | } 269 | } 270 | if (body != null) { 271 | final ArrayList bodyItems = new ArrayList<>(); 272 | final List names = EcoreUtil2.getAllContentsOfType(body, 273 | SimpleExpressionNameValue.class); 274 | for (final SimpleExpressionNameValue name : names) { 275 | for (final SimpleExpressionNameValue item : items) { 276 | if (name.getValue().equalsIgnoreCase(item.getValue())) { 277 | bodyItems.add(name); 278 | } 279 | } 280 | } 281 | items.addAll(bodyItems); 282 | } 283 | return items; 284 | } 285 | 286 | public boolean isAsserted(SimpleExpressionNameValue n) { 287 | EObject obj = EcoreUtil2.getContainerOfType(n, Body.class); 288 | if (obj == null) { 289 | obj = EcoreUtil2.getContainerOfType(n, DeclareSection.class); 290 | } 291 | final Iterable usages = EcoreUtil2 292 | .getAllContentsOfType(obj, SimpleExpressionNameValue.class).stream() 293 | .filter(it -> it.getValue().equalsIgnoreCase(n.getValue())).collect(Collectors.toList()); 294 | for (final SimpleExpressionNameValue usage : usages) { 295 | final String name = getQualifiedFunctionName(usage); 296 | for (final String assertPackage : ASSERT_PACKAGES) { 297 | if (name.toLowerCase().contains(assertPackage + ".")) { 298 | return true; 299 | } 300 | } 301 | } 302 | return false; 303 | } 304 | 305 | public boolean isAssertedInParameterOrConstantsOrVariables(SimpleExpressionNameValue n) { 306 | // check parameter fist 307 | if (isAsserted(n)) { 308 | return true; 309 | } 310 | // check constants and variables with an assignment of the parameter 311 | for (final SimpleExpressionNameValue item : getItemsWithDefaults(n)) { 312 | if (isAsserted(item)) { 313 | return true; 314 | } 315 | } 316 | return false; 317 | } 318 | 319 | public boolean isParameterName(SimpleExpressionNameValue n) { 320 | if (n.eContainer() instanceof FunctionOrParenthesisParameter) { 321 | final FunctionOrParenthesisParameter param = (FunctionOrParenthesisParameter) n.eContainer(); 322 | return param.getParameterName() == n; 323 | } 324 | return false; 325 | } 326 | 327 | public List getRelevantSimpleExpressionNameValues(EObject obj) { 328 | return EcoreUtil2.getAllContentsOfType(obj, SimpleExpressionNameValue.class).stream() 329 | .filter(it -> it != null && !isParameterName(it)).collect(Collectors.toList()); 330 | } 331 | 332 | public void check(SimpleExpressionNameValue n, HashMap expressions) { 333 | if (!isParameterName(n)) { 334 | if (isParameter(n)) { 335 | if (!isAssertedInParameterOrConstantsOrVariables(n)) { 336 | warning(9501, n, n); 337 | return; 338 | } 339 | } 340 | final HashMap recursiveExpr = getSimpleExpressinNamesFromAssignments(n); 341 | final HashMap newExpressions = new HashMap<>(); 342 | newExpressions.putAll(expressions); 343 | newExpressions.putAll(recursiveExpr); 344 | for (final String key : recursiveExpr.keySet()) { 345 | if (expressions.get(key) == null) { 346 | check(recursiveExpr.get(key), newExpressions); 347 | } 348 | } 349 | } 350 | } 351 | 352 | public DeclareSection getDeclareSection(Body body) { 353 | final EObject parent = body.eContainer(); 354 | DeclareSection declareSection; 355 | if (parent instanceof CreateFunction) { 356 | declareSection = ((CreateFunction) parent).getDeclareSection(); 357 | } else if (parent instanceof CreateProcedure) { 358 | declareSection = ((CreateProcedure) parent).getDeclareSection(); 359 | } else if (parent instanceof FuncDeclInType) { 360 | declareSection = ((FuncDeclInType) parent).getDeclareSection(); 361 | } else if (parent instanceof ProcDeclInType) { 362 | declareSection = ((ProcDeclInType) parent).getDeclareSection(); 363 | } else if (parent instanceof ConstructorDeclaration) { 364 | declareSection = ((ConstructorDeclaration) parent).getDeclareSection(); 365 | } else if (parent instanceof PlsqlBlock) { 366 | declareSection = ((PlsqlBlock) parent).getDeclareSection(); 367 | } else if (parent instanceof FunctionDefinition) { 368 | declareSection = ((FunctionDefinition) parent).getDeclareSection(); 369 | } else if (parent instanceof ProcedureDefinition) { 370 | declareSection = ((ProcedureDefinition) parent).getDeclareSection(); 371 | } else { 372 | declareSection = null; 373 | } 374 | 375 | return declareSection; 376 | } 377 | 378 | public Body getBody(EObject obj) { 379 | final EObject parent = obj.eContainer(); 380 | Body body; 381 | if (parent instanceof CreateFunction) { 382 | body = ((CreateFunction) parent).getBody(); 383 | } else if (parent instanceof CreateProcedure) { 384 | body = ((CreateProcedure) parent).getBody(); 385 | } else if (parent instanceof FuncDeclInType) { 386 | body = ((FuncDeclInType) parent).getBody(); 387 | } else if (parent instanceof ProcDeclInType) { 388 | body = ((ProcDeclInType) parent).getBody(); 389 | } else if (parent instanceof ConstructorDeclaration) { 390 | body = ((ConstructorDeclaration) parent).getBody(); 391 | } else if (parent instanceof PlsqlBlock) { 392 | body = ((PlsqlBlock) parent).getBody(); 393 | } else if (parent instanceof FunctionDefinition) { 394 | body = ((FunctionDefinition) parent).getBody(); 395 | } else if (parent instanceof ProcedureDefinition) { 396 | body = ((ProcedureDefinition) parent).getBody(); 397 | } else if (parent == null) { 398 | body = null; 399 | } else { 400 | body = getBody(parent); 401 | } 402 | return body; 403 | } 404 | 405 | public HashMap getSimpleExpressinNamesFromAssignments( 406 | final SimpleExpressionNameValue n) { 407 | final HashMap expressions = new HashMap<>(); 408 | final Body body = EcoreUtil2.getContainerOfType(n, Body.class); 409 | List assignments = EcoreUtil2 410 | .getAllContentsOfType(body, ProcedureCallOrAssignmentStatement.class).stream() 411 | .filter(it -> it.getAssignment() != null).collect(Collectors.toList()); 412 | for (final ProcedureCallOrAssignmentStatement assignment : assignments) { 413 | Expression varName = null; 414 | if (assignment.getProcedureOrTarget() != null) { 415 | varName = assignment.getProcedureOrTarget().getObject(); 416 | } 417 | if (varName instanceof SimpleExpressionNameValue) { 418 | if (((SimpleExpressionNameValue) varName).getValue().equalsIgnoreCase(n.getValue())) { 419 | Condition a = assignment.getAssignment(); 420 | if (a instanceof SimpleExpressionNameValue) { 421 | expressions.put(((SimpleExpressionNameValue) a).getValue().toLowerCase(), 422 | (SimpleExpressionNameValue) a); 423 | } else { 424 | Condition c; 425 | c = assignment.getAssignment(); 426 | for (final SimpleExpressionNameValue name : getRelevantSimpleExpressionNameValues(c)) { 427 | expressions.put(name.getValue().toLowerCase(), name); 428 | } 429 | } 430 | } 431 | } 432 | } 433 | final DeclareSection declareSection = getDeclareSection(body); 434 | if (declareSection != null) { 435 | final Optional variable = EcoreUtil2 436 | .getAllContentsOfType(declareSection, VariableDeclaration.class).stream() 437 | .filter(it -> it.getVariable().getValue().equalsIgnoreCase(n.getValue()) && it.getDefault() != null) 438 | .findFirst(); 439 | if (variable.isPresent()) { 440 | Iterable _relevantSimplExpressionNameValues = getRelevantSimpleExpressionNameValues( 441 | variable.get().getDefault()); 442 | for (final SimpleExpressionNameValue name : _relevantSimplExpressionNameValues) { 443 | expressions.put(name.getValue().toLowerCase(), name); 444 | } 445 | } else { 446 | final Optional constant = EcoreUtil2 447 | .getAllContentsOfType(declareSection, ConstantDeclaration.class).stream() 448 | .filter(it -> it.getConstant().getValue().equalsIgnoreCase(n.getValue()) 449 | && it.getDefault() != null) 450 | .findFirst(); 451 | if (constant.isPresent()) { 452 | for (final SimpleExpressionNameValue name : getRelevantSimpleExpressionNameValues( 453 | constant.get().getDefault())) { 454 | expressions.put(name.getValue().toLowerCase(), name); 455 | } 456 | } 457 | } 458 | } 459 | return expressions; 460 | } 461 | 462 | public void checkAll(EObject obj) { 463 | final HashMap expressions = new HashMap<>(); 464 | if (obj != null) { 465 | if (obj instanceof SimpleExpressionNameValue) { 466 | expressions.putAll(getSimpleExpressinNamesFromAssignments(((SimpleExpressionNameValue) obj))); 467 | if (expressions.size() == 0) { 468 | expressions.put(((SimpleExpressionNameValue) obj).getValue().toLowerCase(), 469 | ((SimpleExpressionNameValue) obj)); 470 | } 471 | } else { 472 | for (final SimpleExpressionNameValue name : getRelevantSimpleExpressionNameValues(obj)) { 473 | expressions.put(name.getValue().toLowerCase(), name); 474 | } 475 | } 476 | } 477 | for (final SimpleExpressionNameValue name : expressions.values()) { 478 | check(name, expressions); 479 | } 480 | } 481 | 482 | @Check 483 | public void checkExecuteImmediate(ExecuteImmediateStatement s) { 484 | checkAll(s.getStatement()); 485 | } 486 | 487 | @Check 488 | public void checkOpenFor(OpenForStatement s) { 489 | if (s.getExpression() != null) { 490 | checkAll(s.getExpression()); 491 | } 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/validators/TrivadisGuidelines3Plus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators; 17 | 18 | import com.trivadis.oracle.plsql.validation.PLSQLCopGuideline; 19 | import com.trivadis.oracle.plsql.validation.PLSQLCopValidator; 20 | import com.trivadis.oracle.plsql.validation.PLSQLValidator; 21 | 22 | import java.lang.reflect.InvocationTargetException; 23 | import java.util.Arrays; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.stream.Collectors; 27 | 28 | import org.eclipse.xtext.validation.AbstractDeclarativeValidator; 29 | import org.eclipse.xtext.validation.ComposedChecks; 30 | 31 | @ComposedChecks(validators = { OverrideTrivadisGuidelines.class, SQLInjection.class, TrivadisPlsqlNaming.class, 32 | Hint.class }) 33 | public class TrivadisGuidelines3Plus extends PLSQLValidator implements PLSQLCopValidator { 34 | private final TrivadisGuidelines3 converter = new TrivadisGuidelines3(); 35 | private HashMap guidelines; 36 | 37 | // TODO: remove when TrivadisGuidelines2 uses new IDs 38 | @Override 39 | public String getGuidelineId(Integer id) { 40 | if (id < 1000) { 41 | return String.format("G-%04d", converter.getNewId(id)); 42 | } else { 43 | return super.getGuidelineId(id); 44 | } 45 | } 46 | 47 | // TODO: remove when TrivadisGuidelines2 uses new IDs 48 | @Override 49 | public String getGuidelineMsg(Integer id) { 50 | if (id < 1000) { 51 | final Integer newId = converter.getNewId(id); 52 | final PLSQLCopGuideline guideline = getGuidelines().get(newId); 53 | String msg = null; 54 | if (guideline != null) { 55 | msg = guideline.getMsg(); 56 | } 57 | return String.format("G-%04d: %s", newId, msg); 58 | } else { 59 | return super.getGuidelineMsg(id); 60 | } 61 | } 62 | 63 | @Override 64 | public HashMap getGuidelines() { 65 | if (guidelines == null) { 66 | guidelines = new HashMap<>(); 67 | final List annotations = Arrays.stream(getClass().getAnnotations()) 68 | .filter(it -> it.annotationType() == ComposedChecks.class).collect(Collectors.toList()).stream() 69 | .map(it -> (ComposedChecks) it).collect(Collectors.toList()); 70 | for (final ComposedChecks annotation : annotations) { 71 | Class[] _validators = annotation.validators(); 72 | for (final Class validator : _validators) { 73 | final AbstractDeclarativeValidator validatorInstance; 74 | try { 75 | validatorInstance = validator.getDeclaredConstructor().newInstance(); 76 | } catch (InstantiationException | IllegalAccessException | InvocationTargetException 77 | | NoSuchMethodException e) { 78 | throw new RuntimeException(e); 79 | } 80 | for (final PLSQLCopGuideline guideline : ((PLSQLCopValidator) validatorInstance).getGuidelines() 81 | .values()) { 82 | if (guideline.getId() < 1000) { 83 | // TODO: remove when TrivadisGuidelines2 uses new IDs 84 | guidelines.put(converter.getNewId(guideline.getId()), guideline); 85 | } else { 86 | guidelines.put(guideline.getId(), guideline); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | return guidelines; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/trivadis/tvdcc/validators/TrivadisPlsqlNaming.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators; 17 | 18 | import com.trivadis.oracle.plsql.plsql.CollectionTypeDefinition; 19 | import com.trivadis.oracle.plsql.plsql.ConstantDeclaration; 20 | import com.trivadis.oracle.plsql.plsql.ConstructorDeclaration; 21 | import com.trivadis.oracle.plsql.plsql.CreateFunction; 22 | import com.trivadis.oracle.plsql.plsql.CreatePackage; 23 | import com.trivadis.oracle.plsql.plsql.CreatePackageBody; 24 | import com.trivadis.oracle.plsql.plsql.CreateType; 25 | import com.trivadis.oracle.plsql.plsql.CursorDeclarationOrDefinition; 26 | import com.trivadis.oracle.plsql.plsql.ExceptionDeclaration; 27 | import com.trivadis.oracle.plsql.plsql.ExitStatement; 28 | import com.trivadis.oracle.plsql.plsql.FuncDeclInType; 29 | import com.trivadis.oracle.plsql.plsql.FunctionDefinition; 30 | import com.trivadis.oracle.plsql.plsql.ParameterDeclaration; 31 | import com.trivadis.oracle.plsql.plsql.PlsqlBlock; 32 | import com.trivadis.oracle.plsql.plsql.ProcDeclInType; 33 | import com.trivadis.oracle.plsql.plsql.ProcedureDefinition; 34 | import com.trivadis.oracle.plsql.plsql.RecordTypeDefinition; 35 | import com.trivadis.oracle.plsql.plsql.SimpleExpressionNameValue; 36 | import com.trivadis.oracle.plsql.plsql.SubTypeDefinition; 37 | import com.trivadis.oracle.plsql.plsql.UserDefinedType; 38 | import com.trivadis.oracle.plsql.plsql.VariableDeclaration; 39 | import com.trivadis.oracle.plsql.plsql.WhileLoopStatement; 40 | import com.trivadis.oracle.plsql.validation.PLSQLCopGuideline; 41 | import com.trivadis.oracle.plsql.validation.PLSQLCopValidator; 42 | import com.trivadis.oracle.plsql.validation.PLSQLValidator; 43 | import com.trivadis.oracle.plsql.validation.Remediation; 44 | import java.io.File; 45 | import java.io.FileInputStream; 46 | import java.io.IOException; 47 | import java.lang.reflect.Field; 48 | import java.util.*; 49 | import java.util.stream.Collectors; 50 | 51 | import org.eclipse.emf.ecore.EObject; 52 | import org.eclipse.emf.ecore.EPackage; 53 | import org.eclipse.xtext.EcoreUtil2; 54 | import org.eclipse.xtext.nodemodel.ICompositeNode; 55 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; 56 | import org.eclipse.xtext.validation.Check; 57 | import org.eclipse.xtext.validation.EValidatorRegistrar; 58 | 59 | public class TrivadisPlsqlNaming extends PLSQLValidator implements PLSQLCopValidator { 60 | private HashMap guidelines; 61 | 62 | public static final String PROPERTIES_FILE_NAME = "TrivadisPlsqlNaming.properties"; 63 | public static final int ISSUE_GLOBAL_VARIABLE_NAME = 9101; 64 | public static final int ISSUE_LOCAL_VARIABLE_NAME = 9102; 65 | public static final int ISSUE_CURSOR_NAME = 9103; 66 | public static final int ISSUE_RECORD_NAME = 9104; 67 | public static final int ISSUE_ARRAY_NAME = 9105; 68 | public static final int ISSUE_OBJECT_NAME = 9106; 69 | public static final int ISSUE_CURSOR_PARAMETER_NAME = 9107; 70 | public static final int ISSUE_IN_PARAMETER_NAME = 9108; 71 | public static final int ISSUE_OUT_PARAMETER_NAME = 9109; 72 | public static final int ISSUE_IN_OUT_PARAMETER_NAME = 9110; 73 | public static final int ISSUE_RECORD_TYPE_NAME = 9111; 74 | public static final int ISSUE_ARRAY_TYPE_NAME = 9112; 75 | public static final int ISSUE_EXCEPTION_NAME = 9113; 76 | public static final int ISSUE_CONSTANT_NAME = 9114; 77 | public static final int ISSUE_SUBTYPE_NAME = 9115; 78 | 79 | // default naming conventions, can be overridden via TrivadisPlsqlNaming.properties 80 | private static String REGEX_GLOBAL_VARIABLE_NAME = "^g_.+$"; 81 | private static String REGEX_LOCAL_VARIABLE_NAME = "^l_.+$"; 82 | private static String REGEX_CURSOR_NAME = "^c_.+$"; 83 | private static String REGEX_RECORD_NAME = "^r_.+$"; 84 | private static String REGEX_ARRAY_NAME = "^t_.+$"; 85 | private static String REGEX_OBJECT_NAME = "^o_.+$"; 86 | private static String REGEX_CURSOR_PARAMETER_NAME = "^p_.+$"; 87 | private static String REGEX_IN_PARAMETER_NAME = "^in_.+$"; 88 | private static String REGEX_OUT_PARAMETER_NAME = "^out_.+$"; 89 | private static String REGEX_IN_OUT_PARAMETER_NAME = "^io_.+$"; 90 | private static String REGEX_RECORD_TYPE_NAME = "^r_.+_type$"; 91 | private static String REGEX_ARRAY_TYPE_NAME = "^t_.+_type$"; 92 | private static String REGEX_EXCEPTION_NAME = "^e_.+$"; 93 | private static String REGEX_CONSTANT_NAME = "^co_.+$"; 94 | private static String REGEX_SUBTYPE_NAME = "^.+_type$"; 95 | 96 | public TrivadisPlsqlNaming() { 97 | super(); 98 | readProperties(); 99 | applySystemProperties(); // this way properties can be passed to the JVM in other ways, e.g. via command-line 100 | } 101 | 102 | // must be overridden to avoid duplicate issues when used via ComposedChecks 103 | @Override 104 | public void register(EValidatorRegistrar registrar) { 105 | final List ePackages = getEPackages(); 106 | if (registrar.getRegistry().get(ePackages.get(0)) == null) { 107 | // standalone validator, default registration required 108 | super.register(registrar); 109 | } 110 | } 111 | 112 | private void readProperties() { 113 | try { 114 | final FileInputStream fis = new FileInputStream( 115 | System.getProperty("user.home") + File.separator + PROPERTIES_FILE_NAME); 116 | final Properties prop = System.getProperties(); 117 | prop.load(fis); 118 | fis.close(); 119 | } catch (IOException e) { 120 | // ignore, see https://github.com/Trivadis/plsql-cop-validators/issues/13 121 | } 122 | } 123 | 124 | private void applySystemProperties() { 125 | final Properties prop = System.getProperties(); 126 | final List fields = Arrays.stream(getClass().getDeclaredFields()) 127 | .filter(it -> it.getName().startsWith("REGEX_")) 128 | .collect(Collectors.toList()); 129 | for (final Field field : fields) { 130 | final Object value = prop.get(field.getName()); 131 | if (value != null) { 132 | try { 133 | field.set(this, prop.get(field.getName())); 134 | } catch (IllegalArgumentException | IllegalAccessException e) { 135 | // ignore 136 | } 137 | } 138 | } 139 | } 140 | 141 | @Override 142 | public HashMap getGuidelines() { 143 | if (guidelines == null) { 144 | guidelines = new HashMap<>(); 145 | // inherit all existing guidelines 146 | for (final Integer k : super.getGuidelines().keySet()) { 147 | guidelines.put(k, super.getGuidelines().get(k)); 148 | } 149 | // register custom guidelines 150 | guidelines.put(ISSUE_GLOBAL_VARIABLE_NAME, 151 | new PLSQLCopGuideline(ISSUE_GLOBAL_VARIABLE_NAME, 152 | "Always name global variables to match '" + REGEX_GLOBAL_VARIABLE_NAME + "'.", MINOR, 153 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 154 | guidelines.put(ISSUE_LOCAL_VARIABLE_NAME, 155 | new PLSQLCopGuideline(ISSUE_LOCAL_VARIABLE_NAME, 156 | "Always name local variables to match '" + REGEX_LOCAL_VARIABLE_NAME + "'.", MINOR, 157 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 158 | guidelines.put(ISSUE_CURSOR_NAME, 159 | new PLSQLCopGuideline(ISSUE_CURSOR_NAME, "Always name cursors to match '" + REGEX_CURSOR_NAME + "'.", 160 | MINOR, UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 161 | guidelines.put(ISSUE_RECORD_NAME, 162 | new PLSQLCopGuideline(ISSUE_RECORD_NAME, "Always name records to match '" + REGEX_RECORD_NAME + "'.", 163 | MINOR, UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 164 | guidelines.put(ISSUE_ARRAY_NAME, 165 | new PLSQLCopGuideline(ISSUE_ARRAY_NAME, 166 | "Always name collection types (arrays/tables) to match '" + REGEX_ARRAY_NAME + "'.", MINOR, 167 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 168 | guidelines.put(ISSUE_OBJECT_NAME, 169 | new PLSQLCopGuideline(ISSUE_OBJECT_NAME, "Always name objects to match '" + REGEX_OBJECT_NAME + "'.", 170 | MINOR, UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 171 | guidelines.put(ISSUE_CURSOR_PARAMETER_NAME, 172 | new PLSQLCopGuideline(ISSUE_CURSOR_PARAMETER_NAME, 173 | "Always name cursor parameters to match '" + REGEX_CURSOR_PARAMETER_NAME + "'.", MINOR, 174 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 175 | guidelines.put(ISSUE_IN_PARAMETER_NAME, 176 | new PLSQLCopGuideline(ISSUE_IN_PARAMETER_NAME, 177 | "Always name in parameters to match '" + REGEX_IN_PARAMETER_NAME + "'.", MINOR, 178 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 179 | guidelines.put(ISSUE_OUT_PARAMETER_NAME, 180 | new PLSQLCopGuideline(ISSUE_OUT_PARAMETER_NAME, 181 | "Always name out parameters to match '" + REGEX_OUT_PARAMETER_NAME + "'.", MINOR, 182 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 183 | guidelines.put(ISSUE_IN_OUT_PARAMETER_NAME, 184 | new PLSQLCopGuideline(ISSUE_IN_OUT_PARAMETER_NAME, 185 | "Always name in/out parameters to match '" + REGEX_IN_OUT_PARAMETER_NAME + "'.", MINOR, 186 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 187 | guidelines.put(ISSUE_RECORD_TYPE_NAME, 188 | new PLSQLCopGuideline(ISSUE_RECORD_TYPE_NAME, 189 | "Always name record type definitions to match '" + REGEX_RECORD_TYPE_NAME + "'.", MINOR, 190 | UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 191 | guidelines.put(ISSUE_ARRAY_TYPE_NAME, 192 | new PLSQLCopGuideline(ISSUE_ARRAY_TYPE_NAME, 193 | "Always name collection type definitions (arrays/tables) to match '" + REGEX_ARRAY_TYPE_NAME 194 | + "'.", MINOR, UNDERSTANDABILITY, Remediation.createConstantPerIssue(1))); 195 | guidelines.put(ISSUE_EXCEPTION_NAME, 196 | new PLSQLCopGuideline(ISSUE_EXCEPTION_NAME, 197 | "Always name exceptions to match '" + REGEX_EXCEPTION_NAME + "'.", MINOR, UNDERSTANDABILITY, 198 | Remediation.createConstantPerIssue(1))); 199 | guidelines.put(ISSUE_CONSTANT_NAME, 200 | new PLSQLCopGuideline(ISSUE_CONSTANT_NAME, 201 | "Always name constants to match '" + REGEX_CONSTANT_NAME + "'.", MINOR, UNDERSTANDABILITY, 202 | Remediation.createConstantPerIssue(1))); 203 | guidelines.put(ISSUE_SUBTYPE_NAME, 204 | new PLSQLCopGuideline(ISSUE_SUBTYPE_NAME, 205 | "Always name subtypes to match '" + REGEX_SUBTYPE_NAME + "'.", MINOR, UNDERSTANDABILITY, 206 | Remediation.createConstantPerIssue(1))); 207 | } 208 | return guidelines; 209 | } 210 | 211 | private boolean isRowtype(EObject obj) { 212 | boolean ret = false; 213 | final List types = EcoreUtil2.getAllContentsOfType(obj, UserDefinedType.class); 214 | if (types.size() > 0) { 215 | if (types.get(0).isRefByRowtype()) { 216 | ret = true; 217 | } 218 | } 219 | return ret; 220 | } 221 | 222 | private boolean isRecordType(EObject obj) { 223 | boolean ret = false; 224 | List allTypes = EcoreUtil2.getAllContentsOfType(obj, UserDefinedType.class); 225 | UserDefinedType type; 226 | type = allTypes.get(0); 227 | if (type != null) { 228 | List rts = EcoreUtil2.getAllContentsOfType(EcoreUtil2.getRootContainer(obj), 229 | RecordTypeDefinition.class); 230 | if (rts.size() > 0) { 231 | final String typeName = type.getUserDefinedType().getNames().get(0).getValue(); 232 | if (rts.stream().anyMatch(it -> it.getType().getValue().compareToIgnoreCase(typeName) == 0)) { 233 | ret = true; 234 | } 235 | } 236 | } 237 | return ret; 238 | } 239 | 240 | private boolean isCollectionType(EObject obj) { 241 | boolean ret = false; 242 | List allTypes = EcoreUtil2.getAllContentsOfType(obj, UserDefinedType.class); 243 | UserDefinedType type; 244 | type = allTypes.get(0); 245 | if (type != null) { 246 | List cts = EcoreUtil2.getAllContentsOfType(EcoreUtil2.getRootContainer(obj), 247 | CollectionTypeDefinition.class); 248 | if (cts.size() > 0) { 249 | final String typeName = type.getUserDefinedType().getNames().get(0).getValue(); 250 | if (cts.stream().anyMatch(it -> it.getType().getValue().compareToIgnoreCase(typeName) == 0)) { 251 | ret = true; 252 | } 253 | } 254 | } 255 | return ret; 256 | } 257 | 258 | private boolean isObjectType(EObject obj) { 259 | boolean ret = false; 260 | List allTypes = EcoreUtil2.getAllContentsOfType(obj, UserDefinedType.class); 261 | UserDefinedType type; 262 | type = allTypes.get(0); 263 | if (type != null) { 264 | List ots = EcoreUtil2.getAllContentsOfType(EcoreUtil2.getRootContainer(obj), CreateType.class) 265 | .stream().filter(it -> it.getObjectTypeDef() != null).collect(Collectors.toList()); 266 | if (ots.size() > 0) { 267 | final String typeName = type.getUserDefinedType().getNames().get(0).getValue(); 268 | if (ots.stream().anyMatch(it -> it.getType().getValue().compareToIgnoreCase(typeName) == 0)) { 269 | ret = true; 270 | } 271 | } 272 | } 273 | return ret; 274 | } 275 | 276 | private boolean isSysRefcursor(VariableDeclaration v) { 277 | final ICompositeNode node = NodeModelUtils.getNode(v.getType()); 278 | return "sys_refcursor".equalsIgnoreCase(node.getText().trim()); 279 | } 280 | 281 | private boolean isQualifiedUdt(EObject obj) { 282 | boolean ret = false; 283 | List allTypes = EcoreUtil2.getAllContentsOfType(obj, UserDefinedType.class); 284 | UserDefinedType type; 285 | type = allTypes.get(0); 286 | if (type != null) { 287 | if (type.getUserDefinedType().getNames().size() > 1) { 288 | ret = true; 289 | } 290 | } 291 | return ret; 292 | } 293 | 294 | private boolean containsSimpleExpressionName(EObject container, String name) { 295 | boolean found = false; 296 | List values = EcoreUtil2.getAllContentsOfType(container, 297 | SimpleExpressionNameValue.class); 298 | for (SimpleExpressionNameValue value : values) { 299 | if (value.getValue().equalsIgnoreCase(name)) { 300 | found = true; 301 | break; 302 | } 303 | } 304 | return found; 305 | } 306 | 307 | protected EObject getParentOfBody(EObject obj) { 308 | EObject ret = obj; 309 | while (!(ret instanceof PlsqlBlock || ret instanceof ConstructorDeclaration || ret instanceof CreateFunction 310 | || ret instanceof CreatePackageBody || ret instanceof FuncDeclInType 311 | || ret instanceof FunctionDefinition || ret instanceof ProcDeclInType 312 | || ret instanceof ProcedureDefinition) && ret != null) { 313 | ret = ret.eContainer(); 314 | } 315 | return ret; 316 | } 317 | 318 | private boolean isLoopIndexVariable(EObject obj) { 319 | if (obj instanceof SimpleExpressionNameValue) { 320 | String varName = ((SimpleExpressionNameValue) obj).getValue(); 321 | EObject container = getParentOfBody(obj); 322 | List whileLoops = EcoreUtil2.getAllContentsOfType(container, 323 | WhileLoopStatement.class); 324 | boolean found = false; 325 | for (WhileLoopStatement whileLoop : whileLoops) { 326 | if (containsSimpleExpressionName(whileLoop.getCondition(), varName)) { 327 | found = true; 328 | break; 329 | } 330 | } 331 | if (!found) { 332 | List exits = EcoreUtil2.getAllContentsOfType(container, ExitStatement.class); 333 | for (ExitStatement exit : exits) { 334 | if (containsSimpleExpressionName(exit.getCondition(), varName)) { 335 | found = true; 336 | break; 337 | } 338 | } 339 | } 340 | return found; 341 | } 342 | return false; 343 | } 344 | 345 | @Check 346 | public void checkVariableName(VariableDeclaration v) { 347 | final EObject parent = v.eContainer().eContainer(); 348 | final String name = v.getVariable().getValue().toLowerCase(); 349 | if (isSysRefcursor(v)) { 350 | if (!name.matches(REGEX_CURSOR_NAME)) { 351 | warning(ISSUE_CURSOR_NAME, v.getVariable(), v); 352 | } 353 | } else if (isObjectType(v)) { 354 | if (!name.matches(REGEX_OBJECT_NAME)) { 355 | warning(ISSUE_OBJECT_NAME, v.getVariable(), v); 356 | } 357 | } else if (isCollectionType(v)) { 358 | if (!name.matches(REGEX_ARRAY_NAME)) { 359 | warning(ISSUE_ARRAY_NAME, v.getVariable(), v); 360 | } 361 | } else if (isRowtype(v) || isRecordType(v)) { 362 | if (!name.matches(REGEX_RECORD_NAME)) { 363 | warning(ISSUE_RECORD_NAME, v.getVariable(), v); 364 | } 365 | } else { 366 | // reduce false positives, skip checking variables base on qualified UDTs 367 | if (!isQualifiedUdt(v)) { 368 | if (parent instanceof CreatePackage || parent instanceof CreatePackageBody) { 369 | if (!name.matches(REGEX_GLOBAL_VARIABLE_NAME)) { 370 | warning(ISSUE_GLOBAL_VARIABLE_NAME, v.getVariable(), v); 371 | } 372 | } else { 373 | // reduce false positives, allow cursor/object/array names and loop indices (e.g. i and j) 374 | if (!name.matches(REGEX_LOCAL_VARIABLE_NAME) && !name.matches(REGEX_CURSOR_NAME) 375 | && !name.matches(REGEX_OBJECT_NAME) && !name.matches(REGEX_ARRAY_NAME) 376 | && !isLoopIndexVariable(v.getVariable())) { 377 | warning(ISSUE_LOCAL_VARIABLE_NAME, v.getVariable(), v); 378 | } 379 | } 380 | } 381 | } 382 | } 383 | 384 | @Check 385 | public void checkCursorName(CursorDeclarationOrDefinition c) { 386 | if (!c.getCursor().getValue().toLowerCase().matches(REGEX_CURSOR_NAME)) { 387 | warning(ISSUE_CURSOR_NAME, c.getCursor()); 388 | } 389 | for (final ParameterDeclaration p : c.getParams()) { 390 | if (!p.getParameter().getValue().toLowerCase().matches(REGEX_CURSOR_PARAMETER_NAME)) { 391 | warning(ISSUE_CURSOR_PARAMETER_NAME, p.getParameter(), p); 392 | } 393 | } 394 | } 395 | 396 | @Check 397 | public void checkParameterName(ParameterDeclaration p) { 398 | final EObject parent = p.eContainer(); 399 | if (!(parent instanceof CursorDeclarationOrDefinition)) { 400 | final String name = p.getParameter().getValue(); 401 | if (!("self".equalsIgnoreCase(name))) { 402 | if (p.isIn() && p.isOut()) { 403 | if (!name.matches(REGEX_IN_OUT_PARAMETER_NAME)) { 404 | warning(ISSUE_IN_OUT_PARAMETER_NAME, p.getParameter(), p); 405 | } 406 | } else if (p.isOut()) { 407 | if (!name.matches(REGEX_OUT_PARAMETER_NAME)) { 408 | warning(ISSUE_OUT_PARAMETER_NAME, p.getParameter(), p); 409 | } 410 | } else { 411 | if (!name.matches(REGEX_IN_PARAMETER_NAME)) { 412 | warning(ISSUE_IN_PARAMETER_NAME, p.getParameter(), p); 413 | } 414 | } 415 | } 416 | } 417 | } 418 | 419 | @Check 420 | public void checkRecordTypeName(RecordTypeDefinition rt) { 421 | final String name = rt.getType().getValue().toLowerCase(); 422 | if (!(name.matches(REGEX_RECORD_TYPE_NAME))) { 423 | warning(ISSUE_RECORD_TYPE_NAME, rt.getType(), rt); 424 | } 425 | } 426 | 427 | @Check 428 | public void checkArrayTypeName(CollectionTypeDefinition ct) { 429 | final String name = ct.getType().getValue().toLowerCase(); 430 | if (!(name.matches(REGEX_ARRAY_TYPE_NAME))) { 431 | warning(ISSUE_ARRAY_TYPE_NAME, ct.getType(), ct); 432 | } 433 | } 434 | 435 | @Check 436 | public void checkExceptionName(ExceptionDeclaration e) { 437 | if (!e.getException().getValue().toLowerCase().matches(REGEX_EXCEPTION_NAME)) { 438 | warning(ISSUE_EXCEPTION_NAME, e.getException(), e); 439 | } 440 | } 441 | 442 | @Check 443 | public void checkConstantName(ConstantDeclaration co) { 444 | if (!co.getConstant().getValue().toLowerCase().matches(REGEX_CONSTANT_NAME)) { 445 | warning(ISSUE_CONSTANT_NAME, co.getConstant(), co); 446 | } 447 | } 448 | 449 | @Check 450 | public void checkSubtypeName(SubTypeDefinition st) { 451 | if (!st.getType().getValue().toLowerCase().matches(REGEX_SUBTYPE_NAME)) { 452 | warning(ISSUE_SUBTYPE_NAME, st.getType(), st); 453 | } 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /src/main/resources/GLP/genmodel/plsqlcop-model.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CHANGEABILITY 6 | Changeability 7 | 8 | ARCHITECTURE_CHANGEABILITY 9 | Architecture changeability 10 | 11 | 12 | DATA_CHANGEABILITY 13 | Data changeability 14 | 15 | 16 | LOGIC_CHANGEABILITY 17 | Logic changeability 18 | 19 | 20 | 21 | EFFICIENCY 22 | Efficiency 23 | 24 | CPU_EFFICIENCY 25 | Cpu efficiency 26 | 27 | 28 | MEMORY_EFFICIENCY 29 | Memory efficiency 30 | 31 | 32 | NETWORK_USE 33 | Network use 34 | 35 | 36 | 37 | MAINTAINABILITY 38 | Maintainability 39 | 40 | READABILITY 41 | Readability 42 | 43 | 44 | UNDERSTANDABILITY 45 | Understandability 46 | 47 | 48 | trivadis 49 | G-0000 50 | 51 | remediationFunction 52 | CONSTANT_ISSUE 53 | 54 | 55 | remediationFactor 56 | 0.0 57 | mn 58 | 59 | 60 | offset 61 | 0.0 62 | mn 63 | 64 | 65 | 66 | 67 | trivadis 68 | G-9001 69 | 70 | remediationFunction 71 | CONSTANT_ISSUE 72 | 73 | 74 | remediationFactor 75 | 0.0 76 | mn 77 | 78 | 79 | offset 80 | 1.0 81 | mn 82 | 83 | 84 | 85 | 86 | trivadis 87 | G-9002 88 | 89 | remediationFunction 90 | CONSTANT_ISSUE 91 | 92 | 93 | remediationFactor 94 | 0.0 95 | mn 96 | 97 | 98 | offset 99 | 1.0 100 | mn 101 | 102 | 103 | 104 | 105 | trivadis 106 | G-9003 107 | 108 | remediationFunction 109 | CONSTANT_ISSUE 110 | 111 | 112 | remediationFactor 113 | 0.0 114 | mn 115 | 116 | 117 | offset 118 | 1.0 119 | mn 120 | 121 | 122 | 123 | 124 | 125 | PORTABILITY 126 | Portability 127 | 128 | COMPILER_RELATED_PORTABILITY 129 | Compiler related portability 130 | 131 | 132 | HARDWARE_RELATED_PORTABILITY 133 | Hardware related portability 134 | 135 | 136 | LANGUAGE_RELATED_PORTABILITY 137 | Language related portability 138 | 139 | 140 | OS_RELATED_PORTABILITY 141 | Os related portability 142 | 143 | 144 | SOFTWARE_RELATED_PORTABILITY 145 | Software related portability 146 | 147 | 148 | TIME_ZONE_RELATED_PORTABILITY 149 | Time zone related portability 150 | 151 | 152 | 153 | RELIABILITY 154 | Reliability 155 | 156 | ARCHITECTURE_RELIABILITY 157 | Architecture reliability 158 | 159 | 160 | ARCHITECTURE_RELIABILITY 161 | Architecture reliability 162 | 163 | 164 | trivadis 165 | E-0001 166 | 167 | remediationFunction 168 | CONSTANT_ISSUE 169 | 170 | 171 | remediationFactor 172 | 0.0 173 | mn 174 | 175 | 176 | offset 177 | 1.0 178 | mn 179 | 180 | 181 | 182 | 183 | trivadis 184 | E-0002 185 | 186 | remediationFunction 187 | CONSTANT_ISSUE 188 | 189 | 190 | remediationFactor 191 | 0.0 192 | mn 193 | 194 | 195 | offset 196 | 1.0 197 | mn 198 | 199 | 200 | 201 | 202 | trivadis 203 | E-0003 204 | 205 | remediationFunction 206 | CONSTANT_ISSUE 207 | 208 | 209 | remediationFactor 210 | 0.0 211 | mn 212 | 213 | 214 | offset 215 | 1.0 216 | mn 217 | 218 | 219 | 220 | 221 | DATA_RELIABILITY 222 | Data reliability 223 | 224 | 225 | EXCEPTION_HANDLING 226 | Exception handling 227 | 228 | 229 | FAULT_TOLERANCE 230 | Fault tolerance 231 | 232 | 233 | INSTRUCTION_RELIABILITY 234 | Instruction reliability 235 | 236 | 237 | LOGIC_RELIABILITY 238 | Logic reliability 239 | 240 | 241 | RESOURCE_RELIABILITY 242 | Resource reliability 243 | 244 | 245 | SYNCHRONIZATION_RELIABILITY 246 | Synchronization reliability 247 | 248 | 249 | UNIT_TESTS 250 | Unit tests 251 | 252 | 253 | 254 | REUSABILITY 255 | Reusability 256 | 257 | MODULARITY 258 | Modularity 259 | 260 | 261 | TRANSPORTABILITY 262 | Transportability 263 | 264 | 265 | 266 | SECURITY 267 | Security 268 | 269 | API_ABUSE 270 | Api abuse 271 | 272 | 273 | ERRORS 274 | Errors 275 | 276 | 277 | INPUT_VALIDATION_AND_REPRESENTATION 278 | Input validation and representation 279 | 280 | 281 | SECURITY_FEATURES 282 | Security features 283 | 284 | 285 | 286 | TESTABILITY 287 | Testability 288 | 289 | INTEGRATION_TESTABILITY 290 | Integration testability 291 | 292 | 293 | UNIT_TESTABILITY 294 | Unit testability 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /src/main/resources/GLP/genmodel/rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | G-0000 6 | 7 | CODE_SMELL 8 | G-0000 9 | INFO 10 | SINGLE 11 | READY 12 |

]]>
14 | avoid 15 |
16 | 17 | G-9001 18 | 19 | CODE_SMELL 20 | G-9001 21 | MINOR 22 | SINGLE 23 | READY 24 | Reason 26 |

To easily distinguish between global and local variables.

27 |

Furthermore, the use of a prefix avoids naming conflicts with reserved words.

28 |

Bad

29 |
create or replace package pkg as
 30 |    global_variable integer;
 31 | end pkg;
 32 | /
33 |

Good

34 |
create or replace package pkg as
 35 |    g_global_variable integer;
 36 | end pkg;
 37 | /
38 |

]]>
39 | always 40 |
41 | 42 | G-9002 43 | 44 | CODE_SMELL 45 | G-9002 46 | MINOR 47 | SINGLE 48 | READY 49 | Reason 51 |

To easily distinguish between global and local variables.

52 |

Furthermore, the use of a prefix avoids naming conflicts with reserved words.

53 |

Bad

54 |
create or replace procedure p as
 55 |    c integer;
 56 | begin
 57 |    null;
 58 | end p;
 59 | /
60 |

Good

61 |
create or replace procedure p as
 62 |    l_something integer;
 63 | begin
 64 |    null;
 65 | end p;
 66 | /
67 |

]]>
68 | always 69 |
70 | 71 | G-9003 72 | 73 | CODE_SMELL 74 | G-9003 75 | MINOR 76 | SINGLE 77 | READY 78 | Reason 80 |

To easily distinguish between parameters and variables.

81 |

Furthermore, the use of a prefix avoids naming conflicts with reserved words.

82 |

Bad

83 |
create or replace procedure p (a in integer, b in varchar2) as
 84 | begin
 85 |    null;
 86 | end p;
 87 | /
88 |

Good

89 |
create or replace procedure p (p_1 in integer, p_2 in varchar2) as
 90 | begin
 91 |    null;
 92 | end p;
 93 | /
94 |

]]>
95 | always 96 |
97 | 98 | E-0001 99 | E-0001: Timeout occurred (after n seconds) during load/parse/validation of resource. 100 | BUG 101 | E-0001 102 | BLOCKER 103 | SINGLE 104 | READY 105 | Reason 107 |

Processing large SQL files takes time. Increase the db* CODECOP timeout parameter to avoid this error. The default timeout per file is 10 seconds.

108 | ]]>
109 | error 110 |
111 | 112 | E-0002 113 | E-0002: Syntax error. Please check the limitations and contact the author if the code can be compiled successfully in your environment. 114 | BUG 115 | E-0002 116 | BLOCKER 117 | SINGLE 118 | READY 119 | Reason 121 |

It's either a db* CODECOP bug or a limitation, if the code compiles successfully in your environment.

122 |

Please note, that syntax errors may lead to false positives. db* CODECOP will therefore not report guideline violations, if a syntax error was encountered in the processed file.

123 | ]]>
124 | error 125 |
126 | 127 | E-0003 128 | E-0003: License limit reached. 129 | BUG 130 | E-0003 131 | BLOCKER 132 | SINGLE 133 | READY 134 | Reason 136 |

A db* CODECOP license key defines limitations for the validity date, the major version, the number of files, the number of lines, the number commands and the number of bytes to be processed. Please contact your sales representative to obtain a less restrictive license.

137 | ]]>
138 | error 139 |
140 |
141 | -------------------------------------------------------------------------------- /src/main/resources/GLP/sample/guideline_9001.sql: -------------------------------------------------------------------------------- 1 | -- G-9001 Always prefix global variables with 'g_'. 2 | 3 | -- Reason 4 | /*

To easily distinguish between global and local variables.

*/ 5 | /*

Furthermore, the use of a prefix avoids naming conflicts with reserved words.

*/ 6 | 7 | -- Bad 8 | create or replace package pkg as 9 | global_variable integer; 10 | end pkg; 11 | / 12 | 13 | -- Good 14 | create or replace package pkg as 15 | g_global_variable integer; 16 | end pkg; 17 | / 18 | -------------------------------------------------------------------------------- /src/main/resources/GLP/sample/guideline_9002.sql: -------------------------------------------------------------------------------- 1 | -- G-9002: Always prefix local variables with 'l_'. 2 | 3 | -- Reason 4 | /*

To easily distinguish between global and local variables.

*/ 5 | /*

Furthermore, the use of a prefix avoids naming conflicts with reserved words.

*/ 6 | 7 | -- Bad 8 | create or replace procedure p as 9 | c integer; 10 | begin 11 | null; 12 | end p; 13 | / 14 | 15 | -- Good 16 | create or replace procedure p as 17 | l_something integer; 18 | begin 19 | null; 20 | end p; 21 | / 22 | -------------------------------------------------------------------------------- /src/main/resources/GLP/sample/guideline_9003.sql: -------------------------------------------------------------------------------- 1 | -- G-9003: Always prefix parameters with 'p_'. 2 | 3 | -- Reason 4 | /*

To easily distinguish between parameters and variables.

*/ 5 | /*

Furthermore, the use of a prefix avoids naming conflicts with reserved words.

*/ 6 | 7 | -- Bad 8 | create or replace procedure p (a in integer, b in varchar2) as 9 | begin 10 | null; 11 | end p; 12 | / 13 | 14 | -- Good 15 | create or replace procedure p (p_1 in integer, p_2 in varchar2) as 16 | begin 17 | null; 18 | end p; 19 | / 20 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9101.sql: -------------------------------------------------------------------------------- 1 | -- G-9101: Always name global variables to match '^g_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_GLOBAL_VARIABLE_NAME.

*/ 6 | 7 | -- Bad 8 | create or replace package body example as 9 | some_name integer; 10 | end example; 11 | / 12 | 13 | -- Good 14 | create or replace package body example as 15 | g_some_name integer; 16 | end example; 17 | / 18 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9102.sql: -------------------------------------------------------------------------------- 1 | -- G-9102: Always name local variables to match '^l_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_LOCAL_VARIABLE_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | some_name integer; 10 | begin 11 | null; 12 | end; 13 | / 14 | 15 | -- Good 16 | declare 17 | l_some_name integer; 18 | begin 19 | null; 20 | end; 21 | / 22 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9103.sql: -------------------------------------------------------------------------------- 1 | -- G-9103: Always name cursors to match '^c_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_CURSOR_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | l_dept sys_refcursor; 10 | begin 11 | null; 12 | end; 13 | / 14 | 15 | -- Good 16 | declare 17 | c_dept sys_refcursor; 18 | begin 19 | null; 20 | end; 21 | / 22 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9104.sql: -------------------------------------------------------------------------------- 1 | -- G-9104: Always name records to match '^r_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_RECORD_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | emp emp%rowtype; 10 | type r_dept_type is record( 11 | deptno number, 12 | dname varchar2(14 char), 13 | loc loc(13 char) 14 | ); 15 | dept r_dept_type; 16 | begin 17 | null; 18 | end; 19 | / 20 | 21 | -- Good 22 | declare 23 | r_emp emp%rowtype; 24 | type r_dept_type is record( 25 | deptno number, 26 | dname varchar2(14 char), 27 | loc loc(13 char) 28 | ); 29 | r_dept r_dept_type; 30 | begin 31 | null; 32 | end; 33 | / 34 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9105.sql: -------------------------------------------------------------------------------- 1 | -- G-9105: Always name collection types (arrays/tables) to match '^t_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_ARRAY_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | type t_varray_type is varray(10) of string; 10 | array1 t_varray_type; 11 | type t_nested_table_type is table of string; 12 | array2 t_nested_table_type; 13 | type t_assoc_array_type is table of string index by pls_integer; 14 | array3 t_assoc_array_type; 15 | begin 16 | null; 17 | end; 18 | / 19 | 20 | -- Good 21 | declare 22 | type t_varray_type is varray(10) of string; 23 | t_array1 t_varray_type; 24 | type t_nested_table_type is table of string; 25 | t_array2 t_nested_table_type; 26 | type t_assoc_array_type is table of string index by pls_integer; 27 | t_array3 t_assoc_array_type; 28 | begin 29 | null; 30 | end; 31 | / 32 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9106.sql: -------------------------------------------------------------------------------- 1 | -- G-9106: Always name objects to match '^o_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_OBJECT_NAME.

*/ 6 | 7 | -- Bad 8 | create or replace type dept_type as object ( 9 | deptno integer, 10 | dname varchar2(14 char), 11 | loc varchar2(13 char) 12 | ); 13 | / 14 | 15 | declare 16 | dept dept_type; 17 | begin 18 | null; 19 | end; 20 | / 21 | 22 | -- Good 23 | create or replace type dept_type as object ( 24 | deptno integer, 25 | dname varchar2(14 char), 26 | loc varchar2(13 char) 27 | ); 28 | / 29 | 30 | declare 31 | o_dept dept_type; 32 | begin 33 | null; 34 | end; 35 | / 36 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9107.sql: -------------------------------------------------------------------------------- 1 | -- G-9107: Always name cursor parameters to match '^p_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_CURSOR_PARAMETER_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | cursor c_emp(in_ename in varchar2) is 10 | select * 11 | from emp 12 | where ename like in_ename; 13 | begin 14 | null; 15 | end; 16 | / 17 | 18 | -- Good 19 | declare 20 | cursor c_emp(p_ename in varchar2) is 21 | select * 22 | from emp 23 | where ename like p_ename; 24 | begin 25 | null; 26 | end; 27 | / 28 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9108.sql: -------------------------------------------------------------------------------- 1 | -- G-9108: Always name in parameters to match '^in_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_IN_PARAMETER_NAME.

*/ 6 | 7 | -- Bad 8 | create or replace package p is 9 | procedure p2(param in integer); 10 | end p; 11 | / 12 | 13 | -- Good 14 | create or replace package p is 15 | procedure p2(in_param in integer); 16 | end p; 17 | / 18 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9109.sql: -------------------------------------------------------------------------------- 1 | -- G-9109: Always name out parameters to match '^out_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_OUT_PARAMETER_NAME.

*/ 6 | 7 | -- Bad 8 | create or replace package p is 9 | procedure p2(param out integer); 10 | end p; 11 | / 12 | 13 | -- Good 14 | create or replace package p is 15 | procedure p2(out_param out integer); 16 | end p; 17 | / 18 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9110.sql: -------------------------------------------------------------------------------- 1 | -- G-9110: Always name in/out parameters to match '^io_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_IN_OUT_PARAMETER_NAME.

*/ 6 | 7 | -- Bad 8 | create or replace package p is 9 | procedure p2(param in out integer); 10 | end p; 11 | / 12 | 13 | -- Good 14 | create or replace package p is 15 | procedure p2(io_param in out integer); 16 | end p; 17 | / 18 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9111.sql: -------------------------------------------------------------------------------- 1 | -- G-9111: Always name record type definitions to match '^r_.+_type$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_RECORD_TYPE_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | type dept_typ is 10 | record( 11 | deptno number, 12 | dname varchar2(14 char), 13 | loc loc(13 char) 14 | ); 15 | begin 16 | null; 17 | end; 18 | / 19 | 20 | -- Good 21 | declare 22 | type r_dept_type is 23 | record( 24 | deptno number, 25 | dname varchar2(14 char), 26 | loc loc(13 char) 27 | ); 28 | begin 29 | null; 30 | end; 31 | / 32 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9112.sql: -------------------------------------------------------------------------------- 1 | -- G-9112: Always name collection type definitions (arrays/tables) to match '^t_.+_type$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_ARRAY_TYPE_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | type t_varray is varray(10) of string; 10 | type nested_table_type is table of string; 11 | type x_assoc_array_y is table of string index by pls_integer; 12 | begin 13 | null; 14 | end; 15 | / 16 | 17 | -- Good 18 | declare 19 | type t_varray_type is varray(10) of string; 20 | type t_nested_table_type is table of string; 21 | type t_assoc_array_type is table of string index by pls_integer; 22 | begin 23 | null; 24 | end; 25 | / 26 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9113.sql: -------------------------------------------------------------------------------- 1 | -- G-9113: Always name exceptions to match '^e_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_EXCEPTION_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | some_name exception; 10 | begin 11 | null; 12 | end; 13 | / 14 | 15 | -- Good 16 | declare 17 | e_some_name exception; 18 | begin 19 | null; 20 | end; 21 | / 22 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9114.sql: -------------------------------------------------------------------------------- 1 | -- G-9114: Always name constants to match '^co_.+$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_CONSTANT_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | maximum constant integer := 1000; 10 | begin 11 | null; 12 | end; 13 | / 14 | 15 | -- Good 16 | declare 17 | co_maximum constant integer := 1000; 18 | begin 19 | null; 20 | end; 21 | / 22 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9115.sql: -------------------------------------------------------------------------------- 1 | -- G-9115: Always name subtypes to match '^.+_type$'. 2 | 3 | -- Reason 4 | /*

See Naming Conventions for PL/SQL.

5 |

You can override the default via system property REGEX_SUBTYPE_NAME.

*/ 6 | 7 | -- Bad 8 | declare 9 | subtype short_text is varchar2(100 char); 10 | begin 11 | null; 12 | end; 13 | / 14 | 15 | -- Good 16 | declare 17 | subtype short_text_type is varchar2(100 char); 18 | begin 19 | null; 20 | end; 21 | / -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9501.sql: -------------------------------------------------------------------------------- 1 | -- G-9501: Never use parameter in string expression of dynamic SQL. Use asserted local variable instead. 2 | 3 | -- Reason 4 | /*

The use of static SQL eliminates the risk of SQL injection. However, if you write 5 | dynamic SQL you are responsible to ensure that the SQL cannot be injected with malicious 6 | SQL statements.

7 |

*/ 8 | 9 | /*

This check looks for unasserted parameters used in execute immediate statements 10 | and open for statements. All parameters used in these statements must be asserted with 11 | one of the subprograms provided by dbms_assert.

*/ 12 | 13 | -- Bad 14 | -- The input parameter in_table_name is copied to the local variable l_table_name and then used 15 | -- without an assert to build the l_sql variable. Hence, the execute immediate statement is 16 | -- considered vulnerable to SQL injection, e.g. by passing DEPT CASCADE CONSTRAINTS. 17 | create or replace package body pkg is 18 | function f (in_table_name in varchar2) return boolean as 19 | co_templ constant varchar2(4000 byte) := 'DROP TABLE #in_table_name# PURGE'; 20 | l_table_name varchar2(128 byte); 21 | l_sql varchar2(4000 byte); 22 | begin 23 | l_table_name := in_table_name; 24 | l_sql := replace(co_templ, '#in_table_name#', l_table_name); 25 | execute immediate l_sql; 26 | return true; 27 | end f; 28 | end pkg; 29 | / 30 | 31 | -- Good 32 | -- SQL injection is not possible, because the input parameter in_table_name is 33 | -- checked/modified with sys.dbms_assert.enquote_name. 34 | create or replace package body pkg is 35 | function f (in_table_name in varchar2) return boolean as 36 | co_templ constant varchar2(4000 byte) := 'DROP TABLE #in_table_name# PURGE'; 37 | l_table_name varchar2(128 byte); 38 | l_sql varchar2(4000 byte); 39 | begin 40 | l_table_name := sys.dbms_assert.enquote_name(in_table_name); 41 | l_sql := replace(co_templ, '#in_table_name#', l_table_name); 42 | execute immediate l_sql; 43 | return true; 44 | end f; 45 | end pkg; 46 | / 47 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9600.sql: -------------------------------------------------------------------------------- 1 | -- G-9600: Never define more than one comment with hints. 2 | 3 | -- Reason 4 | /*

Only the first comment containing hints is considered by the Oracle Database, 5 | therefore all hints violating this rule are treated as ordinary comments.

*/ 6 | 7 | -- Bad 8 | select -- a comment 9 | /*+ full(e) */ 10 | /* another comment */ 11 | --+ full(d) 12 | e.empno, 13 | e.ename, 14 | d.dname 15 | from emp e 16 | join dept d 17 | on d.deptno = e.deptno 18 | where empno > 7900; 19 | 20 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 21 | 22 | /* 23 | ------------------------------------------------ 24 | | Id | Operation | Name | 25 | ------------------------------------------------ 26 | | 0 | SELECT STATEMENT | | 27 | | 1 | NESTED LOOPS | | 28 | | 2 | NESTED LOOPS | | 29 | | 3 | TABLE ACCESS FULL | EMP | 30 | | 4 | INDEX UNIQUE SCAN | PK_DEPT | 31 | | 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 32 | ------------------------------------------------ 33 | 34 | Hint Report (identified by operation id / Query Block Name / Object Alias): 35 | Total hints for statement: 1 36 | --------------------------------------------------------------------------- 37 | 38 | 3 - SEL$58A6D7F6 / E@SEL$1 39 | - full(e) 40 | */ 41 | 42 | -- Better 43 | select -- a comment 44 | /*+ full(e) full(d) */ 45 | /* another comment */ 46 | e.empno, 47 | e.ename, 48 | d.dname 49 | from emp e 50 | join dept d 51 | on d.deptno = e.deptno 52 | where empno > 7900; 53 | 54 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 55 | 56 | /* 57 | ----------------------------------- 58 | | Id | Operation | Name | 59 | ----------------------------------- 60 | | 0 | SELECT STATEMENT | | 61 | | 1 | HASH JOIN | | 62 | | 2 | TABLE ACCESS FULL| EMP | 63 | | 3 | TABLE ACCESS FULL| DEPT | 64 | ----------------------------------- 65 | 66 | Hint Report (identified by operation id / Query Block Name / Object Alias): 67 | Total hints for statement: 2 68 | --------------------------------------------------------------------------- 69 | 70 | 2 - SEL$58A6D7F6 / E@SEL$1 71 | - full(e) 72 | 73 | 3 - SEL$58A6D7F6 / D@SEL$1 74 | - full(d) 75 | */ 76 | 77 | -- Good 78 | -- do not mix single-line and mult-line comments 79 | -- use hints first or hints last, do not hide them within other comments 80 | select --+ full(e) full(d) 81 | -- a comment 82 | -- another comment 83 | e.empno, 84 | e.ename, 85 | d.dname 86 | from emp e 87 | join dept d 88 | on d.deptno = e.deptno 89 | where empno > 7900; 90 | 91 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 92 | 93 | /* 94 | ----------------------------------- 95 | | Id | Operation | Name | 96 | ----------------------------------- 97 | | 0 | SELECT STATEMENT | | 98 | | 1 | HASH JOIN | | 99 | | 2 | TABLE ACCESS FULL| EMP | 100 | | 3 | TABLE ACCESS FULL| DEPT | 101 | ----------------------------------- 102 | 103 | Hint Report (identified by operation id / Query Block Name / Object Alias): 104 | Total hints for statement: 2 105 | --------------------------------------------------------------------------- 106 | 107 | 2 - SEL$58A6D7F6 / E@SEL$1 108 | - full(e) 109 | 110 | 3 - SEL$58A6D7F6 / D@SEL$1 111 | - full(d) 112 | */ -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9601.sql: -------------------------------------------------------------------------------- 1 | -- G-9601: Never use unknown hints. 2 | 3 | -- Reason 4 | /*

Using unknown hints might invalidate all subsequent hints. 5 | This happens when you use for example NOLOGGING. That's expected 6 | and not a bug. See MOS note 285285.1 or bug 8432870 for details.

*/ 7 | 8 | -- Bad 9 | -- "nologging" is not a hint. It does not exist in v$sql_hint. 10 | -- The "append" hint is ignored as a result "LOAD TABLE CONVENTIONAL" is applied. 11 | insert --+ nologging append 12 | into sales_hist 13 | select * from sales; 14 | 15 | select * from dbms_xplan.display_cursor(format => 'basic'); 16 | 17 | /* 18 | ----------------------------------------------- 19 | | Id | Operation | Name | 20 | ----------------------------------------------- 21 | | 0 | INSERT STATEMENT | | 22 | | 1 | LOAD TABLE CONVENTIONAL | SALES_HIST | 23 | | 2 | PARTITION RANGE ALL | | 24 | | 3 | TABLE ACCESS FULL | SALES | 25 | ----------------------------------------------- 26 | */ 27 | 28 | 29 | -- Good 30 | -- "nologging" is applied on the table, however this is in most 31 | -- environments overridden by the force logging option on database level. 32 | -- The "append" hint is applied, as a result "LOAD AS SELECT" is applied. 33 | alter table sales_hist nologging; 34 | 35 | insert --+ append 36 | into sales_hist 37 | select * from sales; 38 | 39 | select * from dbms_xplan.display_cursor(format => 'basic'); 40 | 41 | /* 42 | ------------------------------------------------------- 43 | | Id | Operation | Name | 44 | ------------------------------------------------------- 45 | | 0 | INSERT STATEMENT | | 46 | | 1 | LOAD AS SELECT | SALES_HIST | 47 | | 2 | OPTIMIZER STATISTICS GATHERING | | 48 | | 3 | PARTITION RANGE ALL | | 49 | | 4 | TABLE ACCESS FULL | SALES | 50 | ------------------------------------------------------- 51 | */ -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9602.sql: -------------------------------------------------------------------------------- 1 | -- G-9602: Always use the alias name instead of the table name. 2 | 3 | -- Reason 4 | /*

There are various hints that reference a table or view. Typically, if an alias is defined 5 | for a table/view, but the table/view name is used in the hint, then the hint is 6 | ignored by the Oracle Database.

*/ 7 | 8 | -- Bad 9 | select --+ leading(emp dept) 10 | * 11 | from emp e 12 | join dept d on d.deptno = e.deptno; 13 | 14 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 15 | 16 | /* 17 | ------------------------------------------------ 18 | | Id | Operation | Name | 19 | ------------------------------------------------ 20 | | 0 | SELECT STATEMENT | | 21 | | 1 | MERGE JOIN | | 22 | | 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 23 | | 3 | INDEX FULL SCAN | PK_DEPT | 24 | | 4 | SORT JOIN | | 25 | | 5 | TABLE ACCESS FULL | EMP | 26 | ------------------------------------------------ 27 | 28 | Hint Report (identified by operation id / Query Block Name / Object Alias): 29 | Total hints for statement: 1 (U - Unused (1)) 30 | --------------------------------------------------------------------------- 31 | 32 | 1 - SEL$58A6D7F6 33 | U - leading(emp dept) 34 | */ 35 | 36 | 37 | -- Good 38 | select --+ leading(e d) 39 | * 40 | from emp e 41 | join dept d on d.deptno = e.deptno; 42 | 43 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 44 | 45 | /* 46 | ----------------------------------- 47 | | Id | Operation | Name | 48 | ----------------------------------- 49 | | 0 | SELECT STATEMENT | | 50 | | 1 | HASH JOIN | | 51 | | 2 | TABLE ACCESS FULL| EMP | 52 | | 3 | TABLE ACCESS FULL| DEPT | 53 | ----------------------------------- 54 | 55 | Hint Report (identified by operation id / Query Block Name / Object Alias): 56 | Total hints for statement: 1 57 | --------------------------------------------------------------------------- 58 | 59 | 1 - SEL$58A6D7F6 60 | - leading(e d) 61 | */ 62 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9603.sql: -------------------------------------------------------------------------------- 1 | -- G-9603: Never reference an unknown table/alias. 2 | 3 | -- Reason 4 | /*

There are various hints that reference a table or view. If the table or 5 | view reference in the hint is neither a table name nor an alias, then the hint 6 | is ignored by the Oracle Database.

*/ 7 | 8 | -- Bad 9 | select --+ leading(emps depts) 10 | * 11 | from emp 12 | join dept on emp.deptno = dept.deptno; 13 | 14 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 15 | 16 | /* 17 | ------------------------------------------------ 18 | | Id | Operation | Name | 19 | ------------------------------------------------ 20 | | 0 | SELECT STATEMENT | | 21 | | 1 | MERGE JOIN | | 22 | | 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 23 | | 3 | INDEX FULL SCAN | PK_DEPT | 24 | | 4 | SORT JOIN | | 25 | | 5 | TABLE ACCESS FULL | EMP | 26 | ------------------------------------------------ 27 | 28 | Hint Report (identified by operation id / Query Block Name / Object Alias): 29 | Total hints for statement: 1 (U - Unused (1)) 30 | --------------------------------------------------------------------------- 31 | 32 | 1 - SEL$58A6D7F6 33 | U - leading(emps depts) 34 | */ 35 | 36 | 37 | -- Good 38 | select --+ leading(emp dept) 39 | * 40 | from emp 41 | join dept on emp.deptno = dept.deptno; 42 | 43 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 44 | 45 | /* 46 | ----------------------------------- 47 | ----------------------------------- 48 | | Id | Operation | Name | 49 | ----------------------------------- 50 | | 0 | SELECT STATEMENT | | 51 | | 1 | HASH JOIN | | 52 | | 2 | TABLE ACCESS FULL| EMP | 53 | | 3 | TABLE ACCESS FULL| DEPT | 54 | ----------------------------------- 55 | 56 | Hint Report (identified by operation id / Query Block Name / Object Alias): 57 | Total hints for statement: 1 58 | --------------------------------------------------------------------------- 59 | 60 | 1 - SEL$58A6D7F6 61 | - leading(emp dept) 62 | */ 63 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9604.sql: -------------------------------------------------------------------------------- 1 | -- G-9604: Never use an invalid stats method. 2 | 3 | -- Reason 4 | /*

The syntax for the gather_stats hints is:

5 |
table_stats([<schema>.]<table> <method> [,] <keyword>=<value> [[,] <keyword>=<value>]...)
6 |

Valid methods are:

7 |
    8 |
  • DEFAULT
  • 9 |
  • SET
  • 10 |
  • SCALE
  • 11 |
  • SAMPLE
  • 12 |
13 |

The Oracle Database treats other methods as a syntax error and will ignore the hint.

*/ 14 | 15 | -- Bad 16 | select /*+ table_stats(emp faster rows=14) */ empno, ename 17 | from emp e 18 | where deptno = 20; 19 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 20 | 21 | /* 22 | ---------------------------------- 23 | | Id | Operation | Name | 24 | ---------------------------------- 25 | | 0 | SELECT STATEMENT | | 26 | | 1 | TABLE ACCESS FULL| EMP | 27 | ---------------------------------- 28 | 29 | Hint Report (identified by operation id / Query Block Name / Object Alias): 30 | Total hints for statement: 1 (E - Syntax error (1)) 31 | --------------------------------------------------------------------------- 32 | 33 | 1 - SEL$1 34 | E - table_stats 35 | */ 36 | 37 | -- Good 38 | select /*+ table_stats(emp set rows=14) */ empno, ename 39 | from emp e 40 | where deptno = 20; 41 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 42 | 43 | /* 44 | ---------------------------------- 45 | | Id | Operation | Name | 46 | ---------------------------------- 47 | | 0 | SELECT STATEMENT | | 48 | | 1 | TABLE ACCESS FULL| EMP | 49 | ---------------------------------- 50 | 51 | Hint Report (identified by operation id / Query Block Name / Object Alias): 52 | Total hints for statement: 1 53 | --------------------------------------------------------------------------- 54 | 55 | 0 - STATEMENT 56 | - table_stats(emp set rows=14) 57 | */ 58 | -------------------------------------------------------------------------------- /src/main/resources/TrivadisGuidelines3Plus/sample/guideline_9605.sql: -------------------------------------------------------------------------------- 1 | -- G-9605: Never use an invalid stats keyword. 2 | 3 | -- Reason 4 | /*

The syntax for the gather_stats hints is:

5 |
table_stats([<schema>.]<table> <method> [,] <keyword>=<value> [[,] <keyword>=<value>]...)
6 |

Valid keywords are:

7 |
    8 |
  • ROWS
  • 9 |
  • BLOCKS
  • 10 |
  • ROW_LENGTH
  • 11 |
12 |

The Oracle Database treats other keywords as a syntax error and will ignore the hint.

*/ 13 | 14 | -- Bad 15 | select /*+ table_stats(emp default rec=14 blk=1 rowlen=10) */ empno, ename 16 | from emp e 17 | where deptno = 20; 18 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 19 | 20 | /* 21 | ---------------------------------- 22 | | Id | Operation | Name | 23 | ---------------------------------- 24 | | 0 | SELECT STATEMENT | | 25 | | 1 | TABLE ACCESS FULL| EMP | 26 | ---------------------------------- 27 | 28 | Hint Report (identified by operation id / Query Block Name / Object Alias): 29 | Total hints for statement: 1 (E - Syntax error (1)) 30 | --------------------------------------------------------------------------- 31 | 32 | 1 - SEL$1 33 | E - table_stats 34 | */ 35 | 36 | -- Good 37 | select /*+ table_stats(emp default rows=14 blocks=1 row_length=10) */ empno, ename 38 | from emp e 39 | where deptno = 20; 40 | select * from dbms_xplan.display_cursor(format => 'basic +hint_report'); 41 | 42 | /* 43 | ---------------------------------- 44 | | Id | Operation | Name | 45 | ---------------------------------- 46 | | 0 | SELECT STATEMENT | | 47 | | 1 | TABLE ACCESS FULL| EMP | 48 | ---------------------------------- 49 | 50 | Hint Report (identified by operation id / Query Block Name / Object Alias): 51 | Total hints for statement: 1 52 | --------------------------------------------------------------------------- 53 | 54 | 0 - STATEMENT 55 | - table_stats(emp set rows=14) 56 | */ 57 | -------------------------------------------------------------------------------- /src/test/java/com/trivadis/tvdcc/validators/tests/AbstractValidatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators.tests; 17 | 18 | import com.google.inject.Injector; 19 | import com.trivadis.oracle.plsql.PLSQLStandaloneSetup; 20 | import com.trivadis.oracle.plsql.validation.PLSQLCopGuideline; 21 | import com.trivadis.oracle.plsql.validation.PLSQLValidator; 22 | import com.trivadis.tvdcc.validators.TrivadisPlsqlNaming; 23 | import java.io.ByteArrayInputStream; 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.nio.charset.Charset; 27 | import java.nio.file.Files; 28 | import java.nio.file.Path; 29 | import java.nio.file.Paths; 30 | import java.nio.file.StandardCopyOption; 31 | import java.util.List; 32 | 33 | import org.eclipse.emf.common.util.EList; 34 | import org.eclipse.emf.common.util.URI; 35 | import org.eclipse.emf.ecore.resource.Resource; 36 | import org.eclipse.xtext.resource.XtextResource; 37 | import org.eclipse.xtext.resource.XtextResourceSet; 38 | import org.eclipse.xtext.validation.CheckMode; 39 | import org.eclipse.xtext.validation.Issue; 40 | import org.junit.AfterClass; 41 | import org.junit.BeforeClass; 42 | import org.junit.Test; 43 | 44 | import static org.hamcrest.MatcherAssert.assertThat; 45 | import static org.hamcrest.core.AnyOf.anyOf; 46 | import static org.hamcrest.core.StringStartsWith.startsWith; 47 | 48 | public abstract class AbstractValidatorTest { 49 | private static final String PROPERTIES_FILE_NAME = TrivadisPlsqlNaming.PROPERTIES_FILE_NAME; 50 | public static final String FULL_PROPERTIES_FILE_NAME = ((System.getProperty("user.home") + File.separator) 51 | + PROPERTIES_FILE_NAME); 52 | private static final String FULL_PROPERTIES_FILE_NAME_BACKUP = (FULL_PROPERTIES_FILE_NAME + ".backup"); 53 | 54 | private final Injector injector = new PLSQLStandaloneSetup().createInjectorAndDoEMFRegistration(); 55 | 56 | @BeforeClass 57 | public static void commonSetup() { 58 | stashPropertiesFile(); 59 | } 60 | 61 | public static void stashPropertiesFile() { 62 | try { 63 | final Path file = Path.of(FULL_PROPERTIES_FILE_NAME); 64 | if (Files.exists(file)) { 65 | Files.copy(file, Paths.get(FULL_PROPERTIES_FILE_NAME_BACKUP)); 66 | Files.delete(file); 67 | } 68 | } catch (IOException e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | 73 | @AfterClass 74 | public static void restorePropertiesFile() { 75 | try { 76 | final Path original = Path.of(FULL_PROPERTIES_FILE_NAME_BACKUP); 77 | final Path file = Path.of(FULL_PROPERTIES_FILE_NAME); 78 | if (Files.exists(original)) { 79 | Files.copy(original, file, StandardCopyOption.REPLACE_EXISTING); 80 | Files.delete(original); 81 | } else { 82 | if (Files.exists(file)) { 83 | Files.delete(file); 84 | } 85 | } 86 | } catch (IOException e) { 87 | throw new RuntimeException(e); 88 | } 89 | } 90 | 91 | @Test 92 | public void guidelineTitleStartsWithKeyword() { 93 | var guidelines = getValidator().getGuidelines().values().stream().filter(it -> it.getId() >= 9000).toList(); 94 | for (final PLSQLCopGuideline g : guidelines) { 95 | assertThat('"' + g.getMsg() + "' does not start with keyword", g.getMsg(), 96 | anyOf(startsWith("Always"), startsWith("Never"), startsWith("Avoid"), startsWith("Try"))); 97 | } 98 | } 99 | 100 | public XtextResource parse(String stmt) { 101 | final XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class); 102 | resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE); 103 | resourceSet.addLoadOption(XtextResource.OPTION_ENCODING, Charset.defaultCharset().name()); 104 | final Resource resource = resourceSet.createResource(URI.createURI("dummy:/test.plsql")); 105 | final ByteArrayInputStream input = new ByteArrayInputStream(stmt.getBytes()); 106 | try { 107 | resource.load(input, resourceSet.getLoadOptions()); 108 | } catch (IOException e) { 109 | throw new RuntimeException(e); 110 | } 111 | return ((XtextResource) resource); 112 | } 113 | 114 | public List getIssues(String stmt) { 115 | final XtextResource resource = parse(stmt); 116 | final EList errors = resource.getErrors(); 117 | if (errors.size() > 0) { 118 | final Resource.Diagnostic firstError = errors.get(0); 119 | throw new RuntimeException( 120 | "Syntax error: " + firstError.getMessage() + " at line " + firstError.getLine() + "."); 121 | } 122 | return resource.getResourceServiceProvider().getResourceValidator().validate(resource, CheckMode.ALL, null); 123 | } 124 | 125 | public PLSQLValidator getValidator() { 126 | return injector.getInstance(PLSQLValidator.class); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/com/trivadis/tvdcc/validators/tests/GLPTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators.tests; 17 | 18 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 19 | import com.trivadis.tvdcc.validators.GLP; 20 | 21 | import org.junit.Assert; 22 | import org.junit.BeforeClass; 23 | import org.junit.Test; 24 | 25 | public class GLPTest extends AbstractValidatorTest { 26 | 27 | @BeforeClass 28 | public static void setupValidator() { 29 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(GLP.class); 30 | } 31 | 32 | @Test 33 | public void guidelines() { 34 | var guidelines = getValidator().getGuidelines(); 35 | Assert.assertEquals(3, guidelines.values().stream().filter(it -> it.getId() >= 9000).count()); 36 | } 37 | 38 | @Test 39 | public void procedureOk() { 40 | var stmt = """ 41 | CREATE OR REPLACE PROCEDURE p (p_1 IN INTEGER, p_2 IN VARCHAR2) AS 42 | l_something INTEGER; 43 | BEGIN 44 | NULL; 45 | END p; 46 | """; 47 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().startsWith("G-9")).toList(); 48 | Assert.assertEquals(0, issues.size()); 49 | } 50 | 51 | @Test 52 | public void procedureNok() { 53 | var stmt = """ 54 | CREATE OR REPLACE PROCEDURE p (a IN INTEGER, b IN VARCHAR2) AS 55 | c INTEGER; 56 | BEGIN 57 | NULL; 58 | END p; 59 | """; 60 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().startsWith("G-9")).toList(); 61 | Assert.assertEquals(3, issues.size()); 62 | // a 63 | var issue1 = issues.get(0); 64 | Assert.assertEquals(1, issue1.getLineNumber().intValue()); 65 | Assert.assertEquals(32, issue1.getColumn().intValue()); 66 | Assert.assertEquals(1, issue1.getLength().intValue()); 67 | Assert.assertEquals("G-9003", issue1.getCode()); 68 | Assert.assertEquals("G-9003: Always prefix parameters with 'p_'.", issue1.getMessage()); 69 | Assert.assertEquals("a IN INTEGER", issue1.getData()[0]); 70 | // b 71 | var issue2 = issues.get(1); 72 | Assert.assertEquals(1, issue2.getLineNumber().intValue()); 73 | Assert.assertEquals(46, issue2.getColumn().intValue()); 74 | Assert.assertEquals(1, issue2.getLength().intValue()); 75 | Assert.assertEquals("G-9003", issue2.getCode()); 76 | Assert.assertEquals("G-9003: Always prefix parameters with 'p_'.", issue2.getMessage()); 77 | Assert.assertEquals("b IN VARCHAR2", issue2.getData()[0]); 78 | // c 79 | var issue3 = issues.get(2); 80 | Assert.assertEquals(2, issue3.getLineNumber().intValue()); 81 | Assert.assertEquals(4, issue3.getColumn().intValue()); 82 | Assert.assertEquals(1, issue3.getLength().intValue()); 83 | Assert.assertEquals("G-9002", issue3.getCode()); 84 | Assert.assertEquals("G-9002: Always prefix local variables with 'l_'.", issue3.getMessage()); 85 | Assert.assertEquals("c INTEGER;", issue3.getData()[0]); 86 | } 87 | 88 | @Test 89 | public void packageBodyOk() { 90 | var stmt = """ 91 | CREATE OR REPLACE PACKAGE BODY pkg AS 92 | g_global_variable INTEGER; 93 | 94 | PROCEDURE p ( 95 | p_parameter1 IN INTEGER, 96 | p_parameter2 IN VARCHAR2 97 | ) AS 98 | l_local_variable INTEGER; 99 | BEGIN 100 | NULL; 101 | END p; 102 | 103 | FUNCTION f ( 104 | p_parameter1 IN INTEGER, 105 | p_parameter2 IN INTEGER 106 | ) RETURN INTEGER AS 107 | l_local_variable INTEGER; 108 | BEGIN 109 | RETURN p_parameter1 * p_parameter2; 110 | END f; 111 | END pkg; 112 | """; 113 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().startsWith("G-9")).toList(); 114 | Assert.assertEquals(0, issues.size()); 115 | } 116 | 117 | @Test 118 | public void packageBodyNok() { 119 | var stmt = """ 120 | CREATE OR REPLACE PACKAGE BODY pkg AS 121 | global_variable INTEGER; 122 | 123 | PROCEDURE p ( 124 | parameter1 IN INTEGER, 125 | parameter2 IN VARCHAR2 126 | ) AS 127 | local_variable INTEGER; 128 | BEGIN 129 | NULL; 130 | END p; 131 | 132 | FUNCTION f ( 133 | parameter1 IN INTEGER, 134 | parameter2 IN INTEGER 135 | ) RETURN INTEGER AS 136 | local_variable INTEGER; 137 | BEGIN 138 | RETURN parameter1 * p_parameter2; 139 | END f; 140 | END pkg; 141 | """; 142 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().startsWith("G-9")).toList(); 143 | Assert.assertEquals(7, issues.size()); 144 | // global_variable 145 | var issue1 = issues.get(0); 146 | Assert.assertEquals(2, issue1.getLineNumber().intValue()); 147 | Assert.assertEquals(4, issue1.getColumn().intValue()); 148 | Assert.assertEquals(15, issue1.getLength().intValue()); 149 | Assert.assertEquals("G-9001", issue1.getCode()); 150 | Assert.assertEquals("G-9001: Always prefix global variables with 'g_'.", issue1.getMessage()); 151 | Assert.assertEquals("global_variable INTEGER;", issue1.getData()[0]); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/test/java/com/trivadis/tvdcc/validators/tests/OverrideTrivadisGuidelinesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators.tests; 17 | 18 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 19 | import com.trivadis.tvdcc.validators.OverrideTrivadisGuidelines; 20 | import org.junit.Assert; 21 | import org.junit.BeforeClass; 22 | import org.junit.Test; 23 | 24 | public class OverrideTrivadisGuidelinesTest extends AbstractValidatorTest { 25 | 26 | @BeforeClass 27 | public static void setupValidator() { 28 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(OverrideTrivadisGuidelines.class); 29 | } 30 | 31 | @Test 32 | public void literalInConstantDeclarationsIsOkay() { 33 | var stmt = """ 34 | CREATE PACKAGE pkg AS 35 | co_templ CONSTANT VARCHAR2(4000 BYTE) := 'a text' || ' more text'; 36 | END pkg; 37 | """; 38 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1050")).toList(); 39 | Assert.assertEquals(0, issues.size()); 40 | } 41 | 42 | @Test 43 | public void literalInLoggerCallIsOkay() { 44 | var stmt = """ 45 | BEGIN 46 | logger.log('Hello World'); 47 | END; 48 | """; 49 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1050")).toList(); 50 | Assert.assertEquals(0, issues.size()); 51 | } 52 | 53 | @Test 54 | public void literalInFunctionOfLoggerCallIsOkay() { 55 | var stmt = """ 56 | BEGIN 57 | logger.log(upper('Hello World')); 58 | logger.log(upper('Hello World')); 59 | END; 60 | """; 61 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1050")).toList(); 62 | Assert.assertEquals(0, issues.size()); 63 | } 64 | 65 | @Test 66 | public void literalInPackageFunctionOfLoggerCallIsOkay() { 67 | var stmt = """ 68 | BEGIN 69 | logger.log(x.y('Hello World')); 70 | logger.log(x.y('Hello World')); 71 | END; 72 | """; 73 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1050")).toList(); 74 | Assert.assertEquals(0, issues.size()); 75 | } 76 | 77 | @Test 78 | public void literalInDbmsOutputCallIsNotOkay() { 79 | var stmt = """ 80 | BEGIN 81 | dbms_output.put_line('Hello World'); 82 | dbms_output.put_line('Hello World'); 83 | END; 84 | """; 85 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1050")).toList(); 86 | Assert.assertEquals(2, issues.size()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/com/trivadis/tvdcc/validators/tests/TrivadisGuidelines3PlusTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators.tests; 17 | 18 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 19 | import com.trivadis.tvdcc.validators.TrivadisGuidelines3Plus; 20 | import org.junit.Assert; 21 | import org.junit.BeforeClass; 22 | import org.junit.Test; 23 | 24 | public class TrivadisGuidelines3PlusTest extends AbstractValidatorTest { 25 | 26 | @BeforeClass 27 | public static void setupValidator() { 28 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(TrivadisGuidelines3Plus.class); 29 | } 30 | 31 | @Test 32 | public void guidelines() { 33 | var guidelines = getValidator().getGuidelines(); 34 | Assert.assertEquals(22, guidelines.values().stream().filter(it -> it.getId() >= 9100).toList().size()); // last guideline in v4.2 is G-9040 35 | Assert.assertEquals(125, guidelines.values().stream().filter(it -> it.getId() < 9100).toList().size()); // added G-3182, G-3183 in v4.3, added G-3330, G-4387 in v4.4 36 | Assert.assertEquals(79, guidelines.values().stream().filter(it -> it.getId() < 1000).toList().size()); 37 | } 38 | 39 | @Test 40 | public void getGuidelineId_mapped_via_Trivadis2() { 41 | Assert.assertEquals("G-1010", getValidator().getGuidelineId(1)); 42 | } 43 | 44 | @Test 45 | public void getGuidelineId_of_Trivadis3() { 46 | Assert.assertEquals("G-2130", getValidator().getGuidelineId(2130)); 47 | } 48 | 49 | @Test 50 | public void getGuidelineMsg_mapped_via_Trivadis2() { 51 | Assert.assertEquals("G-1010: Try to label your sub blocks.", getValidator().getGuidelineMsg(1)); 52 | } 53 | 54 | @Test 55 | public void getGuidelineMsg_mapped_via_Trivadis3() { 56 | Assert.assertEquals("G-2130: Try to use subtypes for constructs used often in your code.", 57 | getValidator().getGuidelineMsg(2130)); 58 | } 59 | 60 | // issue avoided by OverrideTrivadisGuidelines (would throw an error via TrivadisGuidelines3) 61 | @Test 62 | public void literalInLoggerCallIsOkay() { 63 | var stmt = """ 64 | BEGIN 65 | logger.log('Hello World'); 66 | END; 67 | """; 68 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1050")).toList(); 69 | Assert.assertEquals(0, issues.size()); 70 | } 71 | 72 | // issue thrown by OverrideTrivadisGuidelines (check in parent) 73 | @Test 74 | public void literalInDbmsOutputCallIsNotOkay() { 75 | var stmt = """ 76 | BEGIN 77 | dbms_output.put_line('Hello World'); 78 | dbms_output.put_line('Hello World'); 79 | END; 80 | """; 81 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1050")).toList(); 82 | Assert.assertEquals(2, issues.size()); 83 | } 84 | 85 | // issue thrown by TrivadisGuidelines3 86 | @Test 87 | public void guideline2230_na() { 88 | var stmt = """ 89 | CREATE OR REPLACE PACKAGE BODY constants_up IS 90 | co_big_increase CONSTANT NUMBER(5,0) := 1; 91 | 92 | FUNCTION big_increase RETURN NUMBER DETERMINISTIC IS 93 | BEGIN 94 | RETURN co_big_increase; 95 | END big_increase; 96 | END constants_up; 97 | / 98 | """; 99 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-2230")).toList(); 100 | Assert.assertEquals(1, issues.size()); 101 | } 102 | 103 | // issue thrown by TrivadisGuidelines2 104 | @Test 105 | public void guideline1010_10() { 106 | var stmt = """ 107 | BEGIN 108 | BEGIN 109 | NULL; 110 | END; 111 | END; 112 | / 113 | """; 114 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-1010")).toList(); 115 | Assert.assertEquals(1, issues.size()); 116 | } 117 | 118 | // issue thrown by SQLInjection 119 | @Test 120 | public void executeImmediateNotAssertedVariable() { 121 | var stmt = """ 122 | CREATE OR REPLACE PROCEDURE p (in_table_name IN VARCHAR2) AS 123 | co_templ CONSTANT VARCHAR2(4000 BYTE) := 'DROP TABLE #in_table_name# PURGE'; 124 | l_table_name VARCHAR2(128 BYTE); 125 | l_sql VARCHAR2(4000 BYTE); 126 | BEGIN 127 | l_table_name := in_table_name; 128 | l_sql := replace(l_templ, '#in_table_name#', l_table_name); 129 | EXECUTE IMMEDIATE l_sql; 130 | END p; 131 | """; 132 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-9501")).toList(); 133 | Assert.assertEquals(1, issues.size()); 134 | } 135 | 136 | // issue thrown by TrivadisPlsqlNaming 137 | @Test 138 | public void globalVariableNok() { 139 | var stmt = """ 140 | CREATE OR REPLACE PACKAGE example AS 141 | some_name INTEGER; 142 | END example; 143 | / 144 | """; 145 | var issues = getIssues(stmt); 146 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9101")).toList().size()); 147 | } 148 | 149 | // issue thrown by Hint 150 | @Test 151 | public void unknownHint() { 152 | var stmt = """ 153 | INSERT /*+ NOLOGGING APPEND */ INTO sales_hist SELECT * FROM sales; 154 | """; 155 | var issues = getIssues(stmt); 156 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9601")).toList().size()); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/test/java/com/trivadis/tvdcc/validators/tests/TrivadisPlsqlNamingPropertiesFileTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators.tests; 17 | 18 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 19 | import com.trivadis.tvdcc.validators.TrivadisPlsqlNaming; 20 | import java.io.BufferedWriter; 21 | import java.io.File; 22 | import java.io.FileWriter; 23 | import java.io.IOException; 24 | import org.junit.Assert; 25 | import org.junit.BeforeClass; 26 | import org.junit.Test; 27 | 28 | public class TrivadisPlsqlNamingPropertiesFileTest extends AbstractValidatorTest { 29 | 30 | @BeforeClass 31 | public static void commonSetup() { 32 | stashPropertiesFile(); 33 | createTestPropertiesFile(); 34 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(TrivadisPlsqlNaming.class); 35 | } 36 | 37 | // create a simple properties file to test with 38 | public static void createTestPropertiesFile() { 39 | try { 40 | final File file = new File(FULL_PROPERTIES_FILE_NAME); 41 | final FileWriter fileWriter = new FileWriter(file, true); 42 | final BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); 43 | bufferedWriter.write("REGEX_LOCAL_VARIABLE_NAME = ^loc_.+$"); 44 | bufferedWriter.newLine(); 45 | bufferedWriter.close(); 46 | fileWriter.close(); 47 | } catch (IOException e) { 48 | throw new RuntimeException(e); 49 | } 50 | } 51 | 52 | // check that old prefix is now not accepted 53 | @Test 54 | public void LocalVariableNok() { 55 | var stmt = """ 56 | CREATE OR REPLACE PACKAGE BODY example AS 57 | PROCEDURE a IS 58 | l_some_name INTEGER; 59 | BEGIN 60 | NULL; 61 | END a; 62 | END example; 63 | """; 64 | var issues = getIssues(stmt); 65 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 66 | } 67 | 68 | // check that new prefix from file is accepted 69 | @Test 70 | public void LocalVariableOk() { 71 | var stmt = """ 72 | CREATE OR REPLACE PACKAGE BODY example AS 73 | PROCEDURE a IS 74 | loc_some_name INTEGER; 75 | BEGIN 76 | NULL; 77 | END a; 78 | END example; 79 | """; 80 | var issues = getIssues(stmt); 81 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 82 | } 83 | 84 | // check that defaults are used if not specified in the properties-file 85 | @Test 86 | public void GlobalVariableNok() { 87 | var stmt = """ 88 | CREATE OR REPLACE PACKAGE example AS 89 | some_name INTEGER; 90 | END example; 91 | / 92 | """; 93 | var issues = getIssues(stmt); 94 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9101")).toList().size()); 95 | } 96 | 97 | // check that defaults are used if not specified in the properties-file 98 | @Test 99 | public void GlobalVariableOk() { 100 | var stmt = """ 101 | CREATE OR REPLACE PACKAGE example AS 102 | g_some_name INTEGER; 103 | END example; 104 | """; 105 | var issues = getIssues(stmt); 106 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9101")).toList().size()); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/com/trivadis/tvdcc/validators/tests/TrivadisPlsqlNamingSystemPropertiesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators.tests; 17 | 18 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 19 | import com.trivadis.tvdcc.validators.TrivadisPlsqlNaming; 20 | import java.io.File; 21 | import org.junit.Assert; 22 | import org.junit.BeforeClass; 23 | import org.junit.Test; 24 | 25 | public class TrivadisPlsqlNamingSystemPropertiesTest extends AbstractValidatorTest { 26 | 27 | @BeforeClass 28 | public static void commonSetup() { 29 | stashPropertiesFile(); 30 | removePropertiesFile(); 31 | System.getProperties().setProperty("REGEX_LOCAL_VARIABLE_NAME", "^local_.+$"); 32 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(TrivadisPlsqlNaming.class); 33 | } 34 | 35 | // remove property file to ensure it cannot be read from the validator 36 | public static void removePropertiesFile() { 37 | final File file = new File(FULL_PROPERTIES_FILE_NAME); 38 | file.delete(); 39 | } 40 | 41 | // check that old prefix is now not accepted 42 | @Test 43 | public void LocalVariableNok() { 44 | var stmt = """ 45 | CREATE OR REPLACE PACKAGE BODY example AS 46 | PROCEDURE a IS 47 | l_some_name INTEGER; 48 | BEGIN 49 | NULL; 50 | END a; 51 | END example; 52 | """; 53 | var issues = getIssues(stmt); 54 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 55 | } 56 | 57 | // check that new prefix from file is accepted 58 | @Test 59 | public void LocalVariableOk() { 60 | var stmt = """ 61 | CREATE OR REPLACE PACKAGE BODY example AS 62 | PROCEDURE a IS 63 | local_some_name INTEGER; 64 | BEGIN 65 | NULL; 66 | END a; 67 | END example; 68 | """; 69 | var issues = getIssues(stmt); 70 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 71 | } 72 | 73 | // check that defaults are used if not specified in the properties-file 74 | @Test 75 | public void GlobalVariableNok() { 76 | var stmt = """ 77 | CREATE OR REPLACE PACKAGE example AS 78 | some_name INTEGER; 79 | END example; 80 | / 81 | """; 82 | var issues = getIssues(stmt); 83 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9101")).toList().size()); 84 | } 85 | 86 | // check that defaults are used if not specified in the properties-file 87 | @Test 88 | public void GlobalVariableOk() { 89 | var stmt = """ 90 | CREATE OR REPLACE PACKAGE example AS 91 | g_some_name INTEGER; 92 | END example; 93 | """; 94 | var issues = getIssues(stmt); 95 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9101")).toList().size()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/trivadis/tvdcc/validators/tests/TrivadisPlsqlNamingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Philipp Salvisberg 3 | * 4 | * Licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 5 | * Unported License (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * https://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.trivadis.tvdcc.validators.tests; 17 | 18 | import com.trivadis.oracle.plsql.validation.PLSQLValidatorPreferences; 19 | import com.trivadis.tvdcc.validators.TrivadisPlsqlNaming; 20 | import org.junit.Assert; 21 | import org.junit.BeforeClass; 22 | import org.junit.Test; 23 | 24 | public class TrivadisPlsqlNamingTest extends AbstractValidatorTest { 25 | 26 | @BeforeClass 27 | public static void setupValidator() { 28 | PLSQLValidatorPreferences.INSTANCE.setValidatorClass(TrivadisPlsqlNaming.class); 29 | } 30 | 31 | @Test 32 | public void globalVariableNok() { 33 | var stmt = """ 34 | CREATE OR REPLACE PACKAGE example AS 35 | some_name INTEGER; 36 | END example; 37 | / 38 | """; 39 | var issues = getIssues(stmt); 40 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9101")).toList().size()); 41 | } 42 | 43 | @Test 44 | public void globalVariableOk() { 45 | var stmt = """ 46 | CREATE OR REPLACE PACKAGE example AS 47 | g_some_name INTEGER; 48 | END example; 49 | """; 50 | var issues = getIssues(stmt); 51 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9101")).toList().size()); 52 | } 53 | 54 | @Test 55 | public void localVariableNok() { 56 | var stmt = """ 57 | CREATE OR REPLACE PACKAGE BODY example AS 58 | PROCEDURE a IS 59 | some_name INTEGER; 60 | BEGIN 61 | NULL; 62 | END a; 63 | END example; 64 | """; 65 | var issues = getIssues(stmt); 66 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 67 | } 68 | 69 | @Test 70 | public void localVariableOk() { 71 | var stmt = """ 72 | CREATE OR REPLACE PACKAGE BODY example AS 73 | PROCEDURE a IS 74 | l_some_name INTEGER; 75 | BEGIN 76 | NULL; 77 | END a; 78 | END example; 79 | """; 80 | var issues = getIssues(stmt); 81 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 82 | } 83 | 84 | @Test 85 | public void localVariableForStrongCursorOk() { 86 | var stmt = """ 87 | declare 88 | type c_emp_type is ref cursor return employees%rowtype; 89 | c_emp c_emp_type; 90 | r_emp employees%rowtype; 91 | begin 92 | open c_emp for select * from employees where employee_id = 100; 93 | fetch c_emp into r_emp; 94 | close c_emp; 95 | sys.dbms_output.put_line('first_name: ' || r_emp.first_name); 96 | end; 97 | """; 98 | var issues = getIssues(stmt); 99 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 100 | } 101 | 102 | @Test 103 | public void localVariableForObjectOk() { 104 | var stmt = """ 105 | declare 106 | o_game game_ot; 107 | begin 108 | o_game := game_ot(); 109 | pkg.proc(o_game); 110 | end; 111 | """; 112 | var issues = getIssues(stmt); 113 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 114 | } 115 | 116 | @Test 117 | public void localVariableForArrayOk() { 118 | var stmt = """ 119 | declare 120 | t_words word_ct; 121 | begin 122 | t_words := word_ct(); 123 | pkg.proc(t_words); 124 | end; 125 | """; 126 | var issues = getIssues(stmt); 127 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 128 | } 129 | 130 | @Test 131 | public void localVariableSingleLetterIOk() { 132 | // use as common index "i" is accepted 133 | var stmt = """ 134 | declare 135 | i pls_integer := 0; 136 | begin 137 | while i < 10 138 | loop 139 | dbms_output.put_line(i); 140 | i := i + 1; 141 | end loop; 142 | end; 143 | """; 144 | var issues = getIssues(stmt); 145 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 146 | } 147 | 148 | @Test 149 | public void localVariableSingleLetterJOk() { 150 | // use as common index "j" is accepted 151 | var stmt = """ 152 | declare 153 | j pls_integer := 0; 154 | begin 155 | while j < 10 156 | loop 157 | dbms_output.put_line(j); 158 | j := j + 1; 159 | end loop; 160 | end; 161 | """; 162 | var issues = getIssues(stmt); 163 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9102")).toList().size()); 164 | } 165 | 166 | @Test 167 | public void cursorNameNok() { 168 | var stmt = """ 169 | DECLARE 170 | CURSOR some_name IS SELECT * FROM emp; 171 | BEGIN 172 | NULL; 173 | END; 174 | """; 175 | var issues = getIssues(stmt); 176 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9103")).toList().size()); 177 | } 178 | 179 | @Test 180 | public void cursorNameOk() { 181 | var stmt = """ 182 | DECLARE 183 | CURSOR c_some_name IS SELECT * FROM emp; 184 | BEGIN 185 | NULL; 186 | END; 187 | """; 188 | var issues = getIssues(stmt); 189 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9103")).toList().size()); 190 | } 191 | 192 | @Test 193 | public void sysrefcursorNameNOk_bug5() { 194 | var stmt = """ 195 | DECLARE 196 | l_dept SYS_REFCURSOR; 197 | BEGIN 198 | NULL; 199 | END; 200 | / 201 | """; 202 | var issues = getIssues(stmt); 203 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9103")).toList().size()); 204 | } 205 | 206 | @Test 207 | public void sysrefcursorNameOk_bug5() { 208 | var stmt = """ 209 | DECLARE 210 | c_dept SYS_REFCURSOR; 211 | BEGIN 212 | NULL; 213 | END; 214 | / 215 | """; 216 | var issues = getIssues(stmt); 217 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9103")).toList().size()); 218 | } 219 | 220 | @Test 221 | public void recordNameNok() { 222 | var stmt = """ 223 | DECLARE 224 | emp emp%ROWTYPE; 225 | TYPE r_dept_type IS RECORD ( 226 | deptno NUMBER, 227 | dname VARCHAR2(14 CHAR), 228 | loc LOC(13 CHAR) 229 | ); 230 | dept r_dept_type; 231 | BEGIN 232 | NULL; 233 | END; 234 | """; 235 | var issues = getIssues(stmt); 236 | Assert.assertEquals(2, issues.stream().filter(it -> it.getCode().equals("G-9104")).toList().size()); 237 | } 238 | 239 | @Test 240 | public void recordNameOk() { 241 | var stmt = """ 242 | DECLARE 243 | r_emp emp%ROWTYPE; 244 | TYPE r_dept_type IS RECORD ( 245 | deptno NUMBER, 246 | dname VARCHAR2(14 CHAR), 247 | loc LOC(13 CHAR) 248 | ); 249 | r_dept r_dept_type; 250 | BEGIN 251 | NULL; 252 | END; 253 | """; 254 | var issues = getIssues(stmt); 255 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9104")).toList().size()); 256 | } 257 | 258 | @Test 259 | public void arrayNameNok() { 260 | var stmt = """ 261 | DECLARE 262 | TYPE t_varray_type IS VARRAY(10) OF STRING; 263 | array1 t_varray_type; 264 | TYPE t_nested_table_type IS TABLE OF STRING; 265 | array2 t_nested_table_type; 266 | TYPE t_assoc_array_type IS TABLE OF STRING INDEX BY PLS_INTEGER; 267 | array3 t_assoc_array_type; 268 | BEGIN 269 | NULL; 270 | END; 271 | """; 272 | var issues = getIssues(stmt); 273 | Assert.assertEquals(3, issues.stream().filter(it -> it.getCode().equals("G-9105")).toList().size()); 274 | } 275 | 276 | @Test 277 | public void arrayNameOk() { 278 | var stmt = """ 279 | DECLARE 280 | TYPE t_varray_type IS VARRAY(10) OF STRING; 281 | t_array1 t_varray_type; 282 | TYPE t_nested_table_type IS TABLE OF STRING; 283 | t_array2 t_nested_table_type; 284 | TYPE t_assoc_array_type IS TABLE OF STRING INDEX BY PLS_INTEGER; 285 | t_array3 t_assoc_array_type; 286 | BEGIN 287 | NULL; 288 | END; 289 | """; 290 | var issues = getIssues(stmt); 291 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9105")).toList().size()); 292 | } 293 | 294 | @Test 295 | public void objectNameNok() { 296 | var stmt = """ 297 | CREATE OR REPLACE TYPE dept_type AS OBJECT ( 298 | deptno INTEGER, 299 | dname VARCHAR2(14 CHAR), 300 | loc VARCHAR2(13 CHAR) 301 | ); 302 | 303 | DECLARE 304 | dept dept_type; 305 | BEGIN 306 | NULL; 307 | END; 308 | """; 309 | var issues = getIssues(stmt); 310 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9106")).toList().size()); 311 | } 312 | 313 | @Test 314 | public void objectNameOk() { 315 | var stmt = """ 316 | CREATE OR REPLACE TYPE dept_type AS OBJECT ( 317 | deptno INTEGER, 318 | dname VARCHAR2(14 CHAR), 319 | loc VARCHAR2(13 CHAR) 320 | ); 321 | 322 | DECLARE 323 | o_dept dept_type; 324 | BEGIN 325 | NULL; 326 | END; 327 | """; 328 | var issues = getIssues(stmt); 329 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9106")).toList().size()); 330 | } 331 | 332 | @Test 333 | public void cursorParameterNameNok() { 334 | var stmt = """ 335 | DECLARE 336 | CURSOR c_emp (x_ename IN VARCHAR2) IS 337 | SELECT * 338 | FROM emp 339 | WHERE ename LIKE x_ename; 340 | BEGIN 341 | NULL; 342 | END; 343 | """; 344 | var issues = getIssues(stmt); 345 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9107")).toList().size()); 346 | } 347 | 348 | @Test 349 | public void cursorParameterNameOk() { 350 | var stmt = """ 351 | DECLARE 352 | CURSOR c_emp (p_ename IN VARCHAR2) IS 353 | SELECT * 354 | FROM emp 355 | WHERE ename LIKE p_ename; 356 | BEGIN 357 | NULL; 358 | END; 359 | """; 360 | var issues = getIssues(stmt); 361 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9107")).toList().size()); 362 | } 363 | 364 | @Test 365 | public void inParameterNameNok() { 366 | var stmt = """ 367 | CREATE PROCEDURE p1 (param INTEGER) IS 368 | BEGIN 369 | NULL; 370 | END p1; 371 | 372 | CREATE PACKAGE p IS 373 | PROCEDURE p2 (param IN INTEGER); 374 | END p; 375 | """; 376 | var issues = getIssues(stmt); 377 | Assert.assertEquals(2, issues.stream().filter(it -> it.getCode().equals("G-9108")).toList().size()); 378 | } 379 | 380 | @Test 381 | public void inParameterNameOk() { 382 | var stmt = """ 383 | CREATE PROCEDURE p1 (in_param INTEGER) IS 384 | BEGIN 385 | NULL; 386 | END p1; 387 | 388 | CREATE PACKAGE p IS 389 | PROCEDURE p2 (in_param IN INTEGER); 390 | END p; 391 | """; 392 | var issues = getIssues(stmt); 393 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9108")).toList().size()); 394 | } 395 | 396 | @Test 397 | public void SelfInParameterNameOk() { 398 | var stmt = """ 399 | CREATE OR REPLACE TYPE rectangle AUTHID definer AS OBJECT ( 400 | rect_length NUMBER, 401 | rect_width NUMBER, 402 | member FUNCTION get_surface ( 403 | self IN rectangle 404 | ) RETURN NUMBER 405 | ); 406 | """; 407 | var issues = getIssues(stmt); 408 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9108")).toList().size()); 409 | } 410 | 411 | @Test 412 | public void outParameterNameNok() { 413 | var stmt = """ 414 | CREATE PROCEDURE p1 (param OUT INTEGER) IS 415 | BEGIN 416 | NULL; 417 | END p1; 418 | 419 | CREATE PACKAGE p IS 420 | PROCEDURE p2 (param OUT INTEGER); 421 | END p; 422 | """; 423 | var issues = getIssues(stmt); 424 | Assert.assertEquals(2, issues.stream().filter(it -> it.getCode().equals("G-9109")).toList().size()); 425 | } 426 | 427 | @Test 428 | public void outParameterNameOk() { 429 | var stmt = """ 430 | CREATE PROCEDURE p1 (out_param OUT INTEGER) IS 431 | BEGIN 432 | NULL; 433 | END p1; 434 | 435 | CREATE PACKAGE p IS 436 | PROCEDURE p2 (out_param OUT INTEGER); 437 | END p; 438 | """; 439 | var issues = getIssues(stmt); 440 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9109")).toList().size()); 441 | } 442 | 443 | @Test 444 | public void inOutParameterNameNok() { 445 | var stmt = """ 446 | CREATE PROCEDURE p1 (param IN OUT INTEGER) IS 447 | BEGIN 448 | NULL; 449 | END p1; 450 | 451 | CREATE PACKAGE p IS 452 | PROCEDURE p2 (param IN OUT INTEGER); 453 | END p; 454 | """; 455 | var issues = getIssues(stmt); 456 | Assert.assertEquals(2, issues.stream().filter(it -> it.getCode().equals("G-9110")).toList().size()); 457 | } 458 | 459 | @Test 460 | public void inOutParameterNameOk() { 461 | var stmt = """ 462 | CREATE PROCEDURE p1 (io_param IN OUT INTEGER) IS 463 | BEGIN 464 | NULL; 465 | END p1; 466 | 467 | CREATE PACKAGE p IS 468 | PROCEDURE p2 (io_param IN OUT INTEGER); 469 | END p; 470 | """; 471 | var issues = getIssues(stmt); 472 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9110")).toList().size()); 473 | } 474 | 475 | @Test 476 | public void SelfInOutParameterNameOk() { 477 | var stmt = """ 478 | CREATE OR REPLACE TYPE rectangle AUTHID definer AS OBJECT ( 479 | rect_length NUMBER, 480 | rect_width NUMBER, 481 | CONSTRUCTOR FUNCTION rectangle ( 482 | self IN OUT NOCOPY rectangle, 483 | in_length_and_width IN NUMBER 484 | ) RETURN SELF AS RESULT 485 | ); 486 | """; 487 | var issues = getIssues(stmt); 488 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9110")).toList().size()); 489 | } 490 | 491 | @Test 492 | public void recordTypeNameNok() { 493 | var stmt = """ 494 | DECLARE 495 | TYPE dept_typ IS RECORD ( 496 | deptno NUMBER, 497 | dname VARCHAR2(14 CHAR), 498 | loc LOC(13 CHAR) 499 | ); 500 | BEGIN 501 | NULL; 502 | END; 503 | """; 504 | var issues = getIssues(stmt); 505 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9111")).toList().size()); 506 | } 507 | 508 | @Test 509 | public void recordTypeNameOk() { 510 | var stmt = """ 511 | DECLARE 512 | TYPE r_dept_type IS RECORD ( 513 | deptno NUMBER, 514 | dname VARCHAR2(14 CHAR), 515 | loc LOC(13 CHAR) 516 | ); 517 | BEGIN 518 | NULL; 519 | END; 520 | """; 521 | var issues = getIssues(stmt); 522 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9111")).toList().size()); 523 | } 524 | 525 | @Test 526 | public void arrayTypeNameNok() { 527 | var stmt = """ 528 | DECLARE 529 | TYPE t_varray IS VARRAY(10) OF STRING; 530 | TYPE nested_table_type IS TABLE OF STRING; 531 | TYPE x_assoc_array_y IS TABLE OF STRING INDEX BY PLS_INTEGER; 532 | BEGIN 533 | NULL; 534 | END; 535 | """; 536 | var issues = getIssues(stmt); 537 | Assert.assertEquals(3, issues.stream().filter(it -> it.getCode().equals("G-9112")).toList().size()); 538 | } 539 | 540 | @Test 541 | public void arrayTypeNameOk() { 542 | var stmt = """ 543 | DECLARE 544 | TYPE t_varray_type IS VARRAY(10) OF STRING; 545 | TYPE t_nested_table_type IS TABLE OF STRING; 546 | TYPE t_assoc_array_type IS TABLE OF STRING INDEX BY PLS_INTEGER; 547 | BEGIN 548 | NULL; 549 | END; 550 | """; 551 | var issues = getIssues(stmt); 552 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9112")).toList().size()); 553 | 554 | } 555 | 556 | @Test 557 | public void exceptionNameNok() { 558 | var stmt = """ 559 | DECLARE 560 | some_name EXCEPTION; 561 | BEGIN 562 | NULL; 563 | END; 564 | """; 565 | var issues = getIssues(stmt); 566 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9113")).toList().size()); 567 | } 568 | 569 | @Test 570 | public void exceptionNameOk() { 571 | var stmt = """ 572 | DECLARE 573 | e_some_name EXCEPTION; 574 | BEGIN 575 | NULL; 576 | END; 577 | """; 578 | var issues = getIssues(stmt); 579 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9113")).toList().size()); 580 | } 581 | 582 | @Test 583 | public void constantNameNok() { 584 | var stmt = """ 585 | DECLARE 586 | maximum CONSTANT INTEGER := 1000; 587 | BEGIN 588 | NULL; 589 | END; 590 | """; 591 | var issues = getIssues(stmt); 592 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9114")).toList().size()); 593 | } 594 | 595 | @Test 596 | public void constantNameOk() { 597 | var stmt = """ 598 | DECLARE 599 | co_maximum CONSTANT INTEGER := 1000; 600 | BEGIN 601 | NULL; 602 | END; 603 | """; 604 | var issues = getIssues(stmt); 605 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9114")).toList().size()); 606 | } 607 | 608 | @Test 609 | public void subtypeNameNok() { 610 | var stmt = """ 611 | DECLARE 612 | SUBTYPE short_text IS VARCHAR2(100 CHAR); 613 | BEGIN 614 | NULL; 615 | END; 616 | """; 617 | var issues = getIssues(stmt); 618 | Assert.assertEquals(1, issues.stream().filter(it -> it.getCode().equals("G-9115")).toList().size()); 619 | } 620 | 621 | @Test 622 | public void subtypeNameOk() { 623 | var stmt = """ 624 | DECLARE 625 | SUBTYPE short_text_type IS VARCHAR2(100 CHAR); 626 | BEGIN 627 | NULL; 628 | END; 629 | """; 630 | var issues = getIssues(stmt); 631 | Assert.assertEquals(0, issues.stream().filter(it -> it.getCode().equals("G-9115")).toList().size()); 632 | } 633 | 634 | // issue 62 https://github.com/Trivadis/plsql-cop-validators/issues/62 635 | 636 | @Test 637 | public void ignorelocalVariableInBasicLoopExitCondition() { 638 | var stmt = """ 639 | declare 640 | a integer := 0; -- violates G-2185, not used in while loop condition nor exit condition, violates G-9102 641 | b integer; -- does not violate G-2185, used in exit condition, violates G-9102 642 | begin 643 | <> 644 | loop 645 | b := a; 646 | exit demo when b = 10; 647 | a := a + 1; 648 | end loop demo; 649 | end; 650 | / 651 | """; 652 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-9102")).toList(); 653 | Assert.assertEquals(1, issues.size()); 654 | Assert.assertEquals(2, issues.get(0).getLineNumber().intValue()); 655 | } 656 | 657 | @Test 658 | public void ignorelocalVariableInWhileLoopCondition() { 659 | var stmt = """ 660 | declare 661 | a pls_integer := 0; 662 | b integer; 663 | begin 664 | while a < 10 665 | loop 666 | b := a; 667 | dbms_output.put_line(b); 668 | a := a + 1; 669 | end loop; 670 | end; 671 | """; 672 | var issues = getIssues(stmt).stream().filter(it -> it.getCode().equals("G-9102")).toList(); 673 | Assert.assertEquals(1, issues.size()); 674 | Assert.assertEquals(3, issues.get(0).getLineNumber().intValue()); 675 | } 676 | 677 | } 678 | --------------------------------------------------------------------------------