001package gudusoft.gsqlparser.dlineage.dataflow.model; 002 003import gudusoft.gsqlparser.EFunctionType; 004import gudusoft.gsqlparser.ESetOperatorType; 005import gudusoft.gsqlparser.nodes.TCaseExpression; 006import gudusoft.gsqlparser.nodes.TFunctionCall; 007import gudusoft.gsqlparser.nodes.TMergeInsertClause; 008import gudusoft.gsqlparser.nodes.TMergeUpdateClause; 009import gudusoft.gsqlparser.nodes.TOutputClause; 010import gudusoft.gsqlparser.nodes.TParseTreeNode; 011import gudusoft.gsqlparser.stmt.TInsertSqlStatement; 012import gudusoft.gsqlparser.stmt.TUpdateSqlStatement; 013 014/** 015 * The kind of a runtime result set, as a pure function of its runtime class, the parse tree node it is attached to 016 * and its explicit descriptors (plan {@code docs/designs/data-lineage/09-sqllr-v2-runtime-driven-coverage-plan-zh.md}, 017 * Section 3.2). 018 * 019 * <p>{@code DataFlowAnalyzer.getResultSetName} decides the same kinds in the same branch order, but it also names the 020 * result set: it allocates display counters ({@code DISPLAY_ID}), fills the name cache ({@code DISPLAY_NAME}) and 021 * sets the target flag from the display options. That method stays as it is, for the legacy output. The SQL-LR 022 * producer classifies with this class only: it allocates nothing, caches nothing, never sets a flag and never 023 * resolves a name, so classifying cannot change a legacy byte or a display number.</p> 024 * 025 * <p>Internal API of the SQL-LR producer.</p> 026 * 027 * @since 4.2.17 028 */ 029public final class ResultSetClassifier { 030 031 /** The kinds, in the order {@code getResultSetName} tests them. */ 032 public enum Kind { 033 /** An ARRAY(...) constructor ({@code ARRAY-n}). */ 034 ARRAY, 035 /** A STRUCT(...) constructor ({@code STRUCT-n}). */ 036 STRUCT, 037 /** An aliased query table ({@code RESULT_OF_<alias>-n}). */ 038 QUERY_TABLE_ALIAS, 039 /** An aliased reference to a CTE ({@code RESULT_OF_<cte>_<alias>-n}). */ 040 CTE_ALIAS, 041 /** An unaliased reference to a CTE ({@code CTE-<cte>-n}). */ 042 CTE, 043 /** The SELECT of an INSERT ... SELECT ({@code INSERT-SELECT-n}). */ 044 INSERT_SELECT, 045 /** The SELECT of an UPDATE ({@code UPDATE-SELECT-n}). */ 046 UPDATE_SELECT, 047 /** A set operation ({@code RESULT_OF_<OPERATOR>-n}); see {@link Result#getSetOperator()}. */ 048 SET_OPERATION, 049 /** The WHEN MATCHED UPDATE of a MERGE ({@code MERGE-UPDATE-n}). */ 050 MERGE_UPDATE, 051 /** An OUTPUT clause ({@code OUTPUT-n}). */ 052 OUTPUT, 053 /** The WHEN NOT MATCHED INSERT of a MERGE ({@code MERGE-INSERT-n}). */ 054 MERGE_INSERT, 055 /** The SET of an UPDATE ({@code UPDATE-SET-n}). */ 056 UPDATE_SET, 057 /** A CASE expression (named after the function). */ 058 CASE, 059 /** A function call (named after the function). */ 060 FUNCTION, 061 /** A PIVOT ({@code PIVOT-TABLE-n}). */ 062 PIVOT, 063 /** An UNPIVOT ({@code UNPIVOT-TABLE-n}). */ 064 UNPIVOT, 065 /** An alias result set ({@code ALIAS-n}). */ 066 ALIAS, 067 /** Any other result set: a select list ({@code RS-n}). */ 068 SELECT_LIST 069 } 070 071 /** A classification: the kind and the descriptors that refine it. Plain values. */ 072 public static final class Result { 073 private final Kind kind; 074 private final ESetOperatorType setOperator; 075 076 Result(Kind kind, ESetOperatorType setOperator) { 077 this.kind = kind; 078 this.setOperator = setOperator; 079 } 080 081 public Kind getKind() { 082 return kind; 083 } 084 085 /** The operator of a {@link Kind#SET_OPERATION}, else {@code null}. */ 086 public ESetOperatorType getSetOperator() { 087 return setOperator; 088 } 089 090 @Override 091 public String toString() { 092 return setOperator == null ? kind.name() : kind.name() + "(" + setOperator.name() + ")"; 093 } 094 } 095 096 private ResultSetClassifier() { 097 } 098 099 /** Classifies a result set; reads only, in {@code getResultSetName}'s branch order. */ 100 public static Result classify(ResultSet rs) { 101 TParseTreeNode gsp = rs.getGspObject(); 102 // the parsed type, never getFunctionType(): its lookup of an unknown type caches a built-in check on the node, 103 // and the lookup never answers array_t or struct_t, so the kind is the same (Codex review of slice 1a, #4) 104 if (gsp instanceof TFunctionCall && ((TFunctionCall) gsp).getParsedFunctionType() == EFunctionType.array_t) { 105 return new Result(Kind.ARRAY, null); 106 } 107 if (gsp instanceof TFunctionCall && ((TFunctionCall) gsp).getParsedFunctionType() == EFunctionType.struct_t) { 108 return new Result(Kind.STRUCT, null); 109 } 110 if (rs instanceof QueryTable) { 111 QueryTable table = (QueryTable) rs; 112 boolean cte = table.getTableObject().getCTE() != null; 113 if (table.getAlias() != null && table.getAlias().trim().length() > 0) { 114 return new Result(cte ? Kind.CTE_ALIAS : Kind.QUERY_TABLE_ALIAS, null); 115 } 116 if (cte) { 117 return new Result(Kind.CTE, null); 118 } 119 } 120 if (rs instanceof SelectResultSet) { 121 Object parent = ((SelectResultSet) rs).getSelectStmt().getParentStmt(); 122 if (parent instanceof TInsertSqlStatement) { 123 return new Result(Kind.INSERT_SELECT, null); 124 } 125 if (parent instanceof TUpdateSqlStatement) { 126 return new Result(Kind.UPDATE_SELECT, null); 127 } 128 } 129 if (rs instanceof SelectSetResultSet) { 130 return new Result(Kind.SET_OPERATION, ((SelectSetResultSet) rs).getSetOperatorType()); 131 } 132 if (gsp instanceof TMergeUpdateClause) { 133 return new Result(Kind.MERGE_UPDATE, null); 134 } 135 if (gsp instanceof TOutputClause) { 136 return new Result(Kind.OUTPUT, null); 137 } 138 if (gsp instanceof TMergeInsertClause) { 139 return new Result(Kind.MERGE_INSERT, null); 140 } 141 if (gsp instanceof TUpdateSqlStatement) { 142 return new Result(Kind.UPDATE_SET, null); 143 } 144 if (gsp instanceof TCaseExpression) { 145 return new Result(Kind.CASE, null); 146 } 147 if (gsp instanceof TFunctionCall || rs instanceof Function) { 148 return new Result(Kind.FUNCTION, null); 149 } 150 if (rs instanceof PivotedTable) { 151 return new Result(((PivotedTable) rs).isUnpivoted() ? Kind.UNPIVOT : Kind.PIVOT, null); 152 } 153 if (rs instanceof Alias) { 154 return new Result(Kind.ALIAS, null); 155 } 156 return new Result(Kind.SELECT_LIST, null); 157 } 158}