All files / src/abap/5_syntax/expressions sql_compare.ts

94.79% Statements 91/96
78.04% Branches 32/41
100% Functions 2/2
94.79% Lines 91/96

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 961x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 54x 54x 54x 54x 54x 3x 3x 3x 51x 54x 47x 47x 46x 54x 43x 51x 43x 43x 43x 51x 43x 43x 43x 46x 46x 54x 2x 2x 2x 2x 2x 2x 2x 1x 1x 2x 2x 45x 54x 54x 36x 36x 36x 36x 36x 36x 1x 36x 35x   35x 35x   35x 35x   35x 35x 35x 2x 35x 33x 33x     36x 3x 3x 36x 54x 1x 1x 36x 36x 36x 8x 8x 36x 28x 28x 1x 1x
import {AbstractToken} from "../../1_lexer/tokens/abstract_token";
import * as Expressions from "../../2_statements/expressions";
import {ExpressionNode, StatementNode} from "../../nodes";
import {CharacterType, IntegerType, NumericType, StructureType, TableType, UnknownType, VoidType} from "../../types/basic";
import {AbstractType} from "../../types/basic/_abstract_type";
import {CurrentScope} from "../_current_scope";
import {DatabaseTableSource} from "./database_table";
import {Dynamic} from "./dynamic";
import {Source} from "./source";
import {SQLSource} from "./sql_source";
 
export class SQLCompare {
 
  public runSyntax(node: ExpressionNode | StatementNode, scope: CurrentScope, filename: string, tables: DatabaseTableSource[]): void {
 
    let sourceType: AbstractType | undefined;
    let token: AbstractToken | undefined;
 
    if (node.getFirstChild()?.get() instanceof Expressions.Dynamic) {
      new Dynamic().runSyntax(node.getFirstChild() as ExpressionNode, scope, filename);
      return;
    }
 
    for (const s of node.findAllExpressions(Expressions.SimpleSource3)) {
      new Source().runSyntax(s, scope, filename);
    }
 
    for (const s of node.findAllExpressions(Expressions.SQLSource)) {
      for (const child of s.getChildren()) {
        if (child instanceof ExpressionNode) {
          token = child.getFirstToken();
          break;
        }
      }
 
      sourceType = new SQLSource().runSyntax(s, scope, filename);
    }
 
    const sqlin = node.findDirectExpression(Expressions.SQLIn);
    if (sqlin && sqlin.getChildren().length === 2) {
      const insource = node.findFirstExpression(Expressions.SQLSource);
      if (insource) {
        const intype = new SQLSource().runSyntax(insource, scope, filename);
        if (intype &&
            !(intype instanceof VoidType) &&
            !(intype instanceof UnknownType) &&
            !(intype instanceof TableType)) {
          throw new Error("IN, not a table");
        }
      }
    }
 
    const fieldName = node.findDirectExpression(Expressions.SQLFieldName)?.concatTokens();
    if (fieldName && sourceType && token) {
// check compatibility for rule sql_value_conversion
      const targetType = this.findType(fieldName, tables, scope);
 
      let message = "";
      if (sourceType instanceof IntegerType
          && targetType instanceof CharacterType) {
        message = "Integer to CHAR conversion";
      } else if (sourceType instanceof IntegerType
          && targetType instanceof NumericType) {
        message = "Integer to NUMC conversion";
      } else if (sourceType instanceof NumericType
          && targetType instanceof IntegerType) {
        message = "NUMC to Integer conversion";
      } else if (sourceType instanceof CharacterType
          && targetType instanceof IntegerType) {
        message = "CHAR to Integer conversion";
      } else if (sourceType instanceof CharacterType
          && targetType instanceof CharacterType
          && sourceType.getLength() > targetType.getLength()) {
        message = "Source field longer than database field, CHAR -> CHAR";
      } else if (sourceType instanceof NumericType
          && targetType instanceof NumericType
          && sourceType.getLength() > targetType.getLength()) {
        message = "Source field longer than database field, NUMC -> NUMC";
      }
      if (message !== "") {
        scope.addSQLConversion(fieldName, message, token);
      }
    }
  }
 
  private findType(fieldName: string, tables: DatabaseTableSource[], scope: CurrentScope): AbstractType | undefined {
    for (const t of tables) {
      const type = t?.parseType(scope.getRegistry());
      if (type instanceof StructureType) {
        return type.getComponentByName(fieldName);
      }
    }
    return undefined;
  }
 
}