All files / src/rules select_add_order_by.ts

100% Statements 95/95
83.78% Branches 31/37
100% Functions 6/6
100% Lines 95/95

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 88 89 90 91 92 93 94 951x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7653x 7653x 7653x 7653x 7653x 22815x 22815x 22815x 22815x 22815x 22815x 22815x 22815x 22815x 22815x 22815x 22815x 22815x 7653x 7653x 7346x 7346x 7653x 7653x 147x 147x 147x 7653x 7653x 139x 139x 7653x 7653x 173x 173x 30x 30x 143x 143x 143x 173x 147x 147x 12x 12x 135x 135x 135x 147x 8x 8x 1x 1x 7x 7x 7x 8x 3x 8x 1x 1x 3x 8x 8x 3x 3x 3x 3x 3x 2x 2x 3x 1x 1x 1x 135x 131x 131x 131x 7653x 7653x
import * as Expressions from "../abap/2_statements/expressions";
import * as Statements from "../abap/2_statements/statements";
import {BasicRuleConfig} from "./_basic_rule_config";
import {Issue} from "../issue";
import {IRule, IRuleMetadata, RuleTag} from "./_irule";
import {SyntaxLogic} from "../abap/5_syntax/syntax";
import {IObject} from "../objects/_iobject";
import {ABAPObject} from "../objects/_abap_object";
import {IRegistry} from "../_iregistry";
import {TableAccessType, TableType} from "../abap/types/basic";
 
export class SelectAddOrderByConf extends BasicRuleConfig {
}
 
export class SelectAddOrderBy implements IRule {
  private reg: IRegistry;
  private conf = new SelectAddOrderByConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "select_add_order_by",
      title: "SELECT add ORDER BY",
      shortDescription: `SELECTs add ORDER BY clause`,
      extendedInformation: `
This will make sure that the SELECT statement returns results in the same sequence on different databases
 
add ORDER BY PRIMARY KEY if in doubt
 
If the target is a sorted/hashed table, no issue is reported`,
      tags: [RuleTag.SingleFile],
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public initialize(reg: IRegistry) {
    this.reg = reg;
    return this;
  }
 
  public setConfig(conf: SelectAddOrderByConf): void {
    this.conf = conf;
  }
 
  public run(obj: IObject): Issue[] {
    const issues: Issue[] = [];
    if (!(obj instanceof ABAPObject) || obj.getType() === "INTF") {
      return [];
    }
 
    const spaghetti = new SyntaxLogic(this.reg, obj).run().spaghetti;
 
    for (const file of obj.getABAPFiles()) {
      const stru = file.getStructure();
      if (stru === undefined) {
        return issues;
      }
 
      const selects = stru.findAllStatements(Statements.Select);
      selects.push(...stru.findAllStatements(Statements.SelectLoop));
      for (const s of selects) {
        const c = s.concatTokens();
        if (c.startsWith("SELECT SINGLE ")) {
          continue;
        }
 
        // skip COUNT(*)
        const list = s.findFirstExpression(Expressions.SQLFieldList);
        if (list?.getChildren().length === 1 && list.getFirstChild()?.get() instanceof Expressions.SQLAggregation) {
          continue;
        } else if (s.findFirstExpression(Expressions.SQLOrderBy)) {
          continue;
        }
 
        const target = s.findFirstExpression(Expressions.SQLIntoTable)?.findFirstExpression(Expressions.Target);
        if (target) {
          const start = target.getFirstToken().getStart();
          const scope = spaghetti.lookupPosition(start, file.getFilename());
          const type = scope?.findWriteReference(start)?.getType();
          if (type instanceof TableType
              && (type?.getAccessType() === TableAccessType.sorted || type?.getAccessType() === TableAccessType.hashed)) {
            continue;
          }
        }
 
        issues.push(Issue.atStatement(file, s, "Add ORDER BY", this.getMetadata().key, this.conf.severity));
      }
    }
 
    return issues;
  }
 
}