All files / src/abap/2_statements _select_reclassify.ts

98.42% Statements 125/127
94.52% Branches 69/73
100% Functions 7/7
98.42% Lines 125/127

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 126 127 1281x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5555x 5555x 8735x 3172x 3172x 8735x 70x 66x 66x 4x 4x 8735x 2x 2x 8735x 423x 423x 8735x 199x 199x 8735x 5290x 5290x 1x 45x 45x 187x 121x 121x 187x 187x 7x 7x 187x 38x 38x 1x 30444x 30444x 53198x 53198x 29135x 53198x 53198x 167520x 167520x 29106x 29106x 30443x 1x 1738x 1738x 1738x 1738x 1738x 447x 447x 1291x 1291x 1291x 1291x 1291x 1291x 1291x 1291x 1291x 1291x 1738x 1738x 487x 1738x 1738x 1738x 1738x 417x 417x 417x 1x 47x 47x 47x 47x 47x 47x 47x 47x 47x 47x 4x 4x 4x 1x 29511x 29511x 29511x 29511x 29511x 29511x 29511x 1738x 1738x   1738x 1288x 1288x 29511x 47x 47x   47x 42x 42x 47x 28181x 28181x  
import * as Statements from "./statements";
import * as Expressions from "./expressions";
import {StatementNode} from "../nodes";
import {TokenNode} from "../nodes";
import {AbstractToken} from "../1_lexer/tokens/abstract_token";
import {ExpressionNode} from "../nodes/expression_node";
import {IStatement} from "./statements/_statement";
import {Expression} from "./combi";
 
type TopChildren = readonly (ExpressionNode | TokenNode)[];
type ExpressionConstructor = new () => Expression;
 
function containsAggregation(children: TopChildren): boolean {
  for (const child of children) {
    if (!(child instanceof ExpressionNode)) {
      continue;
    }
    if (child.get() instanceof Expressions.SQLAggregation) {
      if (child.findDirectExpression(Expressions.SQLOver) === undefined) {
        return true;
      }
      continue;
    }
    if (child.get() instanceof Expressions.SQLSetOpGroup) {
      continue;
    }
    if (child.get() instanceof Expressions.SQLFrom) {
      continue;
    }
    if (containsAggregation(child.getChildren())) {
      return true;
    }
  }
  return false;
}
 
function isCountAllPattern(children: TopChildren): boolean {
  for (const child of children) {
    if (!(child instanceof ExpressionNode)) { continue; }
    const e = child.get();
    if (e instanceof Expressions.SQLGroupBy ||
        e instanceof Expressions.SQLHaving ||
        e instanceof Expressions.SQLOrderBy) {
      return false;
    }
  }
  return containsAggregation(children);
}
 
function scanForExprTypes(children: TopChildren, found: Set<ExpressionConstructor>, targets: readonly ExpressionConstructor[]): void {
  for (const child of children) {
    if (found.size === targets.length) { return; }
    if (!(child instanceof ExpressionNode)) { continue; }
    const e = child.get();
    if (e instanceof Expressions.SQLSetOpGroup) { continue; }
    for (const t of targets) {
      if (e instanceof t) { found.add(t); break; }
    }
    scanForExprTypes(child.getChildren(), found, targets);
  }
}
 
function isSelectLoop(node: StatementNode): boolean {
  const selectExpr = node.findDirectExpression(Expressions.Select);
  const top = selectExpr ? selectExpr.getChildren() : [];
 
  if (top.some(c => c instanceof TokenNode && c.get().getStr().toUpperCase() === "SINGLE")) {
    return false;
  }
 
  const targets = [
    Expressions.SQLSetOp, Expressions.SQLPackageSize, Expressions.SQLIntoTable,
    Expressions.SQLIntoStructure, Expressions.SQLIntoList, Expressions.SQLGroupBy,
  ] as const;
  const found = new Set<ExpressionConstructor>();
  scanForExprTypes(top, found, targets);
 
  const has = (t: ExpressionConstructor) => found.has(t);
 
  if (has(Expressions.SQLPackageSize)) { return true; }
  if (has(Expressions.SQLIntoTable)) { return false; }
 
  const hasInto = has(Expressions.SQLIntoTable) || has(Expressions.SQLIntoStructure) || has(Expressions.SQLIntoList);
  if (!hasInto && isCountAllPattern(top)) { return false; }
  if (has(Expressions.SQLSetOp)) { return true; }
  if (!has(Expressions.SQLGroupBy) && containsAggregation(top)) { return false; }
 
  return true;
}
 
function isWithLoop(node: StatementNode): boolean {
  const selectExpr = node.findDirectExpression(Expressions.Select);
  if (!selectExpr) { return true; }
 
  const targets = [Expressions.SQLPackageSize, Expressions.SQLIntoTable] as const;
  const found = new Set<ExpressionConstructor>();
  scanForExprTypes(selectExpr.getChildren(), found, targets);
 
  if (found.has(Expressions.SQLPackageSize)) { return true; }
  if (found.has(Expressions.SQLIntoTable)) { return false; }
 
  return true;
}
 
export function reclassifySelect(
  node: StatementNode,
  stmt: IStatement,
  pragmas: readonly AbstractToken[],
): StatementNode {
  const children = [...node.getChildren()];
  if (stmt instanceof Statements.Select || stmt instanceof Statements.SelectLoop) {
    const loop = isSelectLoop(node);
    if (loop && stmt instanceof Statements.Select) {
      return new StatementNode(new Statements.SelectLoop(), node.getColon(), pragmas).setChildren(children);
    } else if (!loop && stmt instanceof Statements.SelectLoop) {
      return new StatementNode(new Statements.Select(), node.getColon(), pragmas).setChildren(children);
    }
  } else if (stmt instanceof Statements.With || stmt instanceof Statements.WithLoop) {
    const loop = isWithLoop(node);
    if (loop && stmt instanceof Statements.With) {
      return new StatementNode(new Statements.WithLoop(), node.getColon(), pragmas).setChildren(children);
    } else if (!loop && stmt instanceof Statements.WithLoop) {
      return new StatementNode(new Statements.With(), node.getColon(), pragmas).setChildren(children);
    }
  }
  return node;
}