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

98.34% Statements 119/121
94.2% Branches 65/69
100% Functions 7/7
98.34% Lines 119/121

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 1221x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6405x 6405x 9796x 3775x 3775x 9796x 59x 59x 9796x 2x 2x 9796x 179x 179x 9796x 6167x 6167x 1x 44x 44x 179x 115x 115x 179x 179x 7x 7x 179x 37x 37x 1x 19495x 19495x 33945x 33945x 18630x 33945x 33945x 107474x 107474x 18605x 18605x 19494x 1x 1310x 1310x 1310x 1310x 1310x 442x 442x 868x 868x 868x 868x 868x 868x 868x 868x 868x 868x 1310x 1310x 445x 1310x 1310x 1310x 1310x 382x 382x 382x 1x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 4x 4x 4x 1x 28216x 28216x 28216x 28216x 28216x 28216x 28216x 1310x 1310x   1310x 895x 895x 28216x 22x 22x   22x 17x 17x 22x 27304x 27304x  
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) {
      return true;
    }
    if (child.get() instanceof Expressions.SQLSetOpGroup) {
      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;
}