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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 22555x 22555x 22555x 1x 11278x 11278x 11278x 11278x 11278x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 33676x 11278x 11278x 10798x 10798x 11278x 11278x 240x 240x 240x 11278x 11278x 229x 229x 11278x 11278x 306x 306x 83x 83x 223x 223x 223x 306x 230x 230x 12x 12x 218x 218x 218x 230x 12x 12x 3x 12x 9x 9x 9x 12x 3x 12x 1x 1x 5x 12x 3x 12x 1x 1x 1x 1x 1x 1x 218x 211x 211x 211x 11278x 11278x 5x 5x 5x 5x 5x 5x 5x 5x 1x 1x 1x 1x 5x 5x 5x 5x 3x 3x 5x 2x 2x 11278x 11278x | 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 {StructureType, TableAccessType, TableType} from "../abap/types/basic";
import {StatementNode} from "../abap/nodes";
import {ABAPFile} from "../abap/abap_file";
import {ISpaghettiScope} from "../abap/5_syntax/_spaghetti_scope";
export class SelectAddOrderByConf extends BasicRuleConfig {
public skipForAllEntries: boolean = false;
}
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],
badExample: `SELECT * FROM db INTO TABLE @DATA(tab).`,
goodExample: `SELECT * FROM db INTO TABLE @DATA(tab) ORDER BY PRIMARY KEY.`,
};
}
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 concat = s.concatTokens().toUpperCase();
if (concat.startsWith("SELECT SINGLE ")) {
continue;
} else if (this.getConfig()?.skipForAllEntries === true && concat.includes(" FOR ALL ENTRIES ")) {
continue;
}
// skip COUNT(*)
const list = s.findAllExpressions(Expressions.SQLField);
if (list.length === 1 && list[0].getFirstChild()?.get() instanceof Expressions.SQLAggregation) {
continue;
} else if (s.findFirstExpression(Expressions.SQLOrderBy)) {
continue;
}
if (this.isTargetSortedOrHashed(s, spaghetti, file)) {
continue;
} else if (s.findFirstExpression(Expressions.SQLJoin) && s.findFirstExpression(Expressions.SQLForAllEntries)) {
// see https://github.com/abaplint/abaplint/issues/2957
continue;
}
issues.push(Issue.atStatement(file, s, "Add ORDER BY", this.getMetadata().key, this.conf.severity));
}
}
return issues;
}
private isTargetSortedOrHashed(s: StatementNode, spaghetti: ISpaghettiScope, file: ABAPFile): boolean {
const target = s.findFirstExpression(Expressions.SQLIntoTable)?.findFirstExpression(Expressions.Target);
if (target) {
const start = target.getFirstToken().getStart();
const scope = spaghetti.lookupPosition(start, file.getFilename());
let type = scope?.findWriteReference(start)?.getType();
const children = target.getChildren();
if (type instanceof StructureType && children.length >= 3 && children[1].concatTokens() === "-") {
const found = type.getComponentByName(children[2].concatTokens());
if (found === undefined) {
return false;
}
type = found;
}
if (type instanceof TableType
&& (type?.getAccessType() === TableAccessType.sorted
|| type?.getAccessType() === TableAccessType.hashed)) {
return true;
}
}
return false;
}
} |