All files / src/rules strict_sql.ts

97.72% Statements 86/88
75.86% Branches 22/29
100% Functions 5/5
97.72% Lines 86/88

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 881x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11090x 11090x 11090x 11090x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 11090x 11090x 10568x 10568x 11090x 11090x 244x 244x 11090x 11090x 279x 279x 279x 279x 23x 23x 256x 256x 279x     256x 279x 1406x 1406x 17x 17x 17x 17x 17x 17x 17x 17x 2x 17x 9x 9x 6x 6x 6x 17x 2x 2x 6x 6x 6x 6x 6x 6x 1406x 256x 256x 256x 11090x
import * as Statements from "../abap/2_statements/statements";
import * as Expressions from "../abap/2_statements/expressions";
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {LanguageVersion, Release, releaseAtLeast} from "../version";
import {RuleTag, IRuleMetadata} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {ABAPObject} from "../objects/_abap_object";
import {EditHelper} from "../edit_helper";
 
export class StrictSQLConf extends BasicRuleConfig {
}
 
export class StrictSQL extends ABAPRule {
  private conf = new StrictSQLConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "strict_sql",
      title: "Strict SQL",
      shortDescription: `Strict SQL`,
      extendedInformation: `https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abapinto_clause.htm
 
https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abenopensql_strict_mode_750.htm
 
Also see separate rule sql_escape_host_variables
 
Activates from v750 and up`,
      tags: [RuleTag.Upport, RuleTag.Quickfix],
      badExample: `SELECT * FROM ztabl INTO TABLE @rt_content WHERE type = @iv_type ORDER BY PRIMARY KEY.`,
      goodExample: `SELECT * FROM ztabl WHERE type = @iv_type ORDER BY PRIMARY KEY INTO TABLE @rt_content.`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: StrictSQLConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile, obj: ABAPObject) {
    const issues: Issue[] = [];
 
    const type = obj.getType();
    if (type === "INTF" || type === "TYPE") {
      return [];
    }
 
    if (!releaseAtLeast(this.reg.getConfig().getRelease(), Release.v750)
        && this.reg.getConfig().getLanguageVersion() !== LanguageVersion.Cloud) {
      return [];
    }
 
    for (const s of file.getStatements()) {
      if (s.get() instanceof Statements.Select
          || s.get() instanceof Statements.SelectLoop) {
 
        const expr = s.findDirectExpression(Expressions.Select);
        const where = expr?.findDirectExpression(Expressions.SQLCond);
        const order = expr?.findDirectExpression(Expressions.SQLOrderBy);
        const into = expr?.findDirectExpression(Expressions.SQLIntoStructure)
          || expr?.findDirectExpression(Expressions.SQLIntoList)
          || expr?.findDirectExpression(Expressions.SQLIntoTable);
        if (into === undefined || where === undefined) {
          continue;
        } else if (where.getFirstToken().getStart().isBefore(into.getFirstToken().getStart())) {
          continue;
        }
 
        const fix1 = EditHelper.deleteRange(file, into.getFirstToken().getStart(), into.getLastToken().getEnd());
        let last = where.getLastToken();
        if (order && order.getLastToken().getEnd().isAfter(last.getEnd())) {
          last = order.getLastToken();
        }
        const fix2 = EditHelper.insertAt(file, last.getEnd(), " " + into.concatTokens());
        const fix = EditHelper.merge(fix2, fix1);
        const message = "INTO/APPENDING must be last in strict SQL";
        const issue = Issue.atToken(file, s.getFirstToken(), message, this.getMetadata().key, this.conf.severity, fix);
        issues.push(issue);
      }
    }
 
    return issues;
  }
}