typesql-cli 0.18.3 → 0.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/code-generator2.d.ts.map +1 -1
- package/code-generator2.js +14 -2
- package/code-generator2.js.map +1 -1
- package/dialects/postgres.d.ts +2 -1
- package/dialects/postgres.d.ts.map +1 -1
- package/dialects/postgres.js +18 -1
- package/dialects/postgres.js.map +1 -1
- package/drivers/postgres.d.ts +4 -1
- package/drivers/postgres.d.ts.map +1 -1
- package/drivers/postgres.js +21 -0
- package/drivers/postgres.js.map +1 -1
- package/drivers/types.d.ts +4 -2
- package/drivers/types.d.ts.map +1 -1
- package/mysql-query-analyzer/select-columns.d.ts +1 -0
- package/mysql-query-analyzer/select-columns.d.ts.map +1 -1
- package/mysql-query-analyzer/select-columns.js +13 -0
- package/mysql-query-analyzer/select-columns.js.map +1 -1
- package/package.json +1 -1
- package/postgres-query-analyzer/case-nullability-checker.d.ts +3 -0
- package/postgres-query-analyzer/case-nullability-checker.d.ts.map +1 -0
- package/postgres-query-analyzer/case-nullability-checker.js +110 -0
- package/postgres-query-analyzer/case-nullability-checker.js.map +1 -0
- package/postgres-query-analyzer/describe.d.ts.map +1 -1
- package/postgres-query-analyzer/describe.js +9 -8
- package/postgres-query-analyzer/describe.js.map +1 -1
- package/postgres-query-analyzer/enum-parser.d.ts +2 -1
- package/postgres-query-analyzer/enum-parser.d.ts.map +1 -1
- package/postgres-query-analyzer/enum-parser.js.map +1 -1
- package/postgres-query-analyzer/nullability-checker.d.ts +3 -0
- package/postgres-query-analyzer/nullability-checker.d.ts.map +1 -0
- package/postgres-query-analyzer/nullability-checker.js +110 -0
- package/postgres-query-analyzer/nullability-checker.js.map +1 -0
- package/postgres-query-analyzer/parser.d.ts +3 -2
- package/postgres-query-analyzer/parser.d.ts.map +1 -1
- package/postgres-query-analyzer/parser.js +19 -5
- package/postgres-query-analyzer/parser.js.map +1 -1
- package/postgres-query-analyzer/traverse.d.ts +19 -8
- package/postgres-query-analyzer/traverse.d.ts.map +1 -1
- package/postgres-query-analyzer/traverse.js +488 -174
- package/postgres-query-analyzer/traverse.js.map +1 -1
- package/postgres-query-analyzer/types.d.ts +8 -0
- package/postgres-query-analyzer/types.d.ts.map +1 -1
- package/sqlite-query-analyzer/replace-list-params.d.ts +0 -1
- package/sqlite-query-analyzer/replace-list-params.d.ts.map +1 -1
- package/sqlite-query-analyzer/replace-list-params.js +0 -57
- package/sqlite-query-analyzer/replace-list-params.js.map +1 -1
- package/sqlite-query-analyzer/types.d.ts +8 -2
- package/sqlite-query-analyzer/types.d.ts.map +1 -1
@@ -2,21 +2,23 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.defaultOptions = defaultOptions;
|
4
4
|
exports.traverseSmt = traverseSmt;
|
5
|
+
exports.parseReturnType = parseReturnType;
|
5
6
|
const PostgreSQLParser_1 = require("@wsporto/typesql-parser/postgres/PostgreSQLParser");
|
6
7
|
const typesql_parser_1 = require("@wsporto/typesql-parser");
|
7
8
|
const select_columns_1 = require("../mysql-query-analyzer/select-columns");
|
9
|
+
const postgres_1 = require("@wsporto/typesql-parser/postgres");
|
10
|
+
const case_nullability_checker_1 = require("./case-nullability-checker");
|
8
11
|
function defaultOptions() {
|
9
12
|
return {
|
10
13
|
collectNestedInfo: false,
|
11
14
|
collectDynamicQueryInfo: false
|
12
15
|
};
|
13
16
|
}
|
14
|
-
function traverseSmt(stmt, dbSchema, checkConstraints, options) {
|
17
|
+
function traverseSmt(stmt, dbSchema, checkConstraints, userFunctions, options) {
|
15
18
|
const { collectNestedInfo = false, collectDynamicQueryInfo = false } = options;
|
16
19
|
const traverseResult = {
|
17
20
|
columnsNullability: [],
|
18
|
-
parameters: []
|
19
|
-
singleRow: false
|
21
|
+
parameters: []
|
20
22
|
};
|
21
23
|
if (collectNestedInfo) {
|
22
24
|
traverseResult.relations = [];
|
@@ -35,7 +37,10 @@ function traverseSmt(stmt, dbSchema, checkConstraints, options) {
|
|
35
37
|
collectNestedInfo,
|
36
38
|
collectDynamicQueryInfo,
|
37
39
|
fromColumns: [],
|
38
|
-
|
40
|
+
parentColumns: [],
|
41
|
+
withColumns: [],
|
42
|
+
checkConstraints,
|
43
|
+
userFunctions
|
39
44
|
};
|
40
45
|
const selectstmt = stmt.selectstmt();
|
41
46
|
if (selectstmt) {
|
@@ -82,15 +87,15 @@ function getInParameterList(ctx) {
|
|
82
87
|
}
|
83
88
|
function traverseSelectstmt(selectstmt, context, traverseResult) {
|
84
89
|
const paramIsListResult = getInParameterList(selectstmt);
|
85
|
-
const
|
90
|
+
const selectResult = traverse_selectstmt(selectstmt, context, traverseResult);
|
86
91
|
//select parameters are collected after from paramters
|
87
92
|
traverseResult.parameters.sort((param1, param2) => param1.paramIndex - param2.paramIndex);
|
88
|
-
const multipleRowsResult = !isSingleRowResult(selectstmt,
|
93
|
+
const multipleRowsResult = !(selectResult.singleRow || isSingleRowResult(selectstmt, selectResult.columns));
|
89
94
|
const limit = checkLimit(selectstmt);
|
90
95
|
const postgresTraverseResult = {
|
91
96
|
queryType: 'Select',
|
92
97
|
multipleRowsResult,
|
93
|
-
columns,
|
98
|
+
columns: selectResult.columns,
|
94
99
|
parametersNullability: traverseResult.parameters.map(param => (Object.assign({ isNotNull: param.isNotNull }, addConstraintIfNotNull(param.checkConstraint)))),
|
95
100
|
parameterList: paramIsListResult,
|
96
101
|
limit
|
@@ -108,22 +113,23 @@ function traverse_selectstmt(selectstmt, context, traverseResult) {
|
|
108
113
|
if (select_no_parens) {
|
109
114
|
return traverse_select_no_parens(select_no_parens, context, traverseResult);
|
110
115
|
}
|
111
|
-
|
116
|
+
//select_with_parens
|
117
|
+
return {
|
118
|
+
columns: [],
|
119
|
+
singleRow: true
|
120
|
+
};
|
112
121
|
}
|
113
122
|
function traverse_select_no_parens(select_no_parens, context, traverseResult) {
|
114
|
-
let withColumns = [];
|
115
123
|
const with_clause = select_no_parens.with_clause();
|
116
124
|
if (with_clause) {
|
117
125
|
with_clause.cte_list().common_table_expr_list()
|
118
126
|
.forEach(common_table_expr => {
|
119
|
-
const
|
120
|
-
|
121
|
-
withColumns.push(...withResult);
|
127
|
+
const withResult = traverse_common_table_expr(common_table_expr, context, traverseResult);
|
128
|
+
context.withColumns.push(...withResult);
|
122
129
|
});
|
123
130
|
}
|
124
131
|
const select_clause = select_no_parens.select_clause();
|
125
|
-
const
|
126
|
-
const selectResult = traverse_select_clause(select_clause, newContext, traverseResult);
|
132
|
+
const selectResult = traverse_select_clause(select_clause, context, traverseResult);
|
127
133
|
const select_limit = select_no_parens.select_limit();
|
128
134
|
if (select_limit) {
|
129
135
|
const numParamsBefore = traverseResult.parameters.length;
|
@@ -159,8 +165,8 @@ function traverse_common_table_expr(common_table_expr, context, traverseResult)
|
|
159
165
|
const tableName = common_table_expr.name().getText();
|
160
166
|
const select_stmt = common_table_expr.preparablestmt().selectstmt();
|
161
167
|
const numParamsBefore = traverseResult.parameters.length;
|
162
|
-
const
|
163
|
-
const columnsWithTalbeName = columns.map(col => (Object.assign(Object.assign({}, col), { table_name: tableName })));
|
168
|
+
const selectResult = traverse_selectstmt(select_stmt, Object.assign(Object.assign({}, context), { collectDynamicQueryInfo: false }), traverseResult);
|
169
|
+
const columnsWithTalbeName = selectResult.columns.map(col => (Object.assign(Object.assign({}, col), { table_name: tableName })));
|
164
170
|
if (context.collectDynamicQueryInfo) {
|
165
171
|
const parameters = traverseResult.parameters.slice(numParamsBefore).map((_, index) => index + numParamsBefore);
|
166
172
|
(_a = traverseResult.dynamicQueryInfo) === null || _a === void 0 ? void 0 : _a.with.push({
|
@@ -173,45 +179,55 @@ function traverse_common_table_expr(common_table_expr, context, traverseResult)
|
|
173
179
|
}
|
174
180
|
function traverse_select_clause(select_clause, context, traverseResult) {
|
175
181
|
const simple_select_intersect_list = select_clause.simple_select_intersect_list();
|
176
|
-
|
177
|
-
|
178
|
-
selectColumns = traverse_simple_select_intersect(simple_select_intersect_list[0], context, traverseResult);
|
179
|
-
}
|
182
|
+
const mainSelectResult = traverse_simple_select_intersect(simple_select_intersect_list[0], context, traverseResult);
|
183
|
+
let columns = mainSelectResult.columns;
|
180
184
|
//union
|
181
185
|
for (let index = 1; index < simple_select_intersect_list.length; index++) {
|
182
|
-
const
|
183
|
-
|
184
|
-
const col = Object.assign({ column_name: value.column_name, is_nullable: value.is_nullable ||
|
186
|
+
const unionResult = traverse_simple_select_intersect(simple_select_intersect_list[index], context, traverseResult);
|
187
|
+
columns = columns.map((value, columnIndex) => {
|
188
|
+
const col = Object.assign({ column_name: value.column_name, is_nullable: value.is_nullable || unionResult.columns[columnIndex].is_nullable, table_name: '', table_schema: '', type: value.type }, (value.column_default && { column_default: value.column_default }));
|
185
189
|
return col;
|
186
190
|
});
|
187
191
|
}
|
188
|
-
return
|
192
|
+
return {
|
193
|
+
columns,
|
194
|
+
singleRow: simple_select_intersect_list.length == 1 ? mainSelectResult.singleRow : false
|
195
|
+
};
|
189
196
|
}
|
190
197
|
function traverse_simple_select_intersect(simple_select_intersect, context, traverseResult) {
|
191
198
|
const simple_select_pramary = simple_select_intersect.simple_select_pramary_list()[0];
|
192
199
|
if (simple_select_pramary) {
|
193
200
|
return traverse_simple_select_pramary(simple_select_pramary, context, traverseResult);
|
194
201
|
}
|
195
|
-
return
|
202
|
+
return {
|
203
|
+
columns: [],
|
204
|
+
singleRow: true
|
205
|
+
};
|
196
206
|
}
|
197
207
|
function traverse_simple_select_pramary(simple_select_pramary, context, traverseResult) {
|
198
208
|
var _a, _b, _c, _d, _e;
|
199
|
-
|
209
|
+
let fromResult = {
|
210
|
+
columns: [],
|
211
|
+
singleRow: false
|
212
|
+
};
|
200
213
|
const from_clause = simple_select_pramary.from_clause();
|
201
214
|
if (from_clause) {
|
202
215
|
const where_clause = simple_select_pramary.where_clause();
|
203
|
-
|
204
|
-
|
205
|
-
fromColumns.push(...fieldsNotNull);
|
216
|
+
fromResult = traverse_from_clause(from_clause, context, traverseResult);
|
217
|
+
fromResult.columns = where_clause != null ? fromResult.columns.map(field => checkIsNullable(where_clause, field)) : fromResult.columns;
|
206
218
|
}
|
207
219
|
const values_clause = simple_select_pramary.values_clause();
|
208
220
|
if (values_clause) {
|
209
221
|
const valuesColumns = traverse_values_clause(values_clause, context, traverseResult);
|
210
|
-
return
|
222
|
+
return {
|
223
|
+
columns: valuesColumns,
|
224
|
+
singleRow: false
|
225
|
+
};
|
211
226
|
}
|
212
227
|
const where_a_expr = (_a = simple_select_pramary.where_clause()) === null || _a === void 0 ? void 0 : _a.a_expr();
|
213
228
|
//fromColumns has precedence
|
214
|
-
|
229
|
+
//context.fromColumns only becase of insert. update
|
230
|
+
const newContext = Object.assign(Object.assign({}, context), { fromColumns: [...context.fromColumns, ...fromResult.columns, ...context.parentColumns] });
|
215
231
|
if (where_a_expr) {
|
216
232
|
const numParamsBefore = traverseResult.parameters.length;
|
217
233
|
traverse_a_expr(where_a_expr, newContext, traverseResult);
|
@@ -236,7 +252,10 @@ function traverse_simple_select_pramary(simple_select_pramary, context, traverse
|
|
236
252
|
traverse_a_expr(having_expr, newContext, traverseResult);
|
237
253
|
}
|
238
254
|
const filteredColumns = filterColumns_simple_select_pramary(simple_select_pramary, newContext, traverseResult);
|
239
|
-
return
|
255
|
+
return {
|
256
|
+
columns: filteredColumns,
|
257
|
+
singleRow: fromResult.singleRow
|
258
|
+
};
|
240
259
|
}
|
241
260
|
function extractRelations(a_expr) {
|
242
261
|
const columnsRef = collectContextsOfType(a_expr, PostgreSQLParser_1.ColumnrefContext);
|
@@ -340,7 +359,7 @@ function traverse_target_el(target_el, context, traverseResult) {
|
|
340
359
|
parameters
|
341
360
|
});
|
342
361
|
}
|
343
|
-
return Object.assign({ column_name: alias || exprResult.column_name, is_nullable: exprResult.is_nullable
|
362
|
+
return Object.assign(Object.assign(Object.assign({ column_name: alias || exprResult.column_name, is_nullable: exprResult.is_nullable, table_name: exprResult.table_name, table_schema: exprResult.table_schema, type: exprResult.type }, (exprResult.column_default != null) && { column_default: exprResult.column_default }), (exprResult.column_key != null && { column_key: exprResult.column_key })), (exprResult.jsonType != null && { jsonType: exprResult.jsonType }));
|
344
363
|
}
|
345
364
|
throw Error('Column not found');
|
346
365
|
}
|
@@ -470,6 +489,7 @@ function traverse_expr_is_not(a_expr_is_not, context, traverseResult) {
|
|
470
489
|
const result = traverse_expr_compare(a_expr_compare, context, traverseResult);
|
471
490
|
if (a_expr_is_not.IS() && a_expr_is_not.NULL_P()) {
|
472
491
|
checkParamterNullability(result, traverseResult);
|
492
|
+
return Object.assign(Object.assign({}, result), { is_nullable: false });
|
473
493
|
}
|
474
494
|
return result;
|
475
495
|
}
|
@@ -518,10 +538,10 @@ function traverse_expr_compare(a_expr_compare, context, traverseResult) {
|
|
518
538
|
const result = traverse_select_with_parens(select_with_parens, context, traverseResult);
|
519
539
|
return {
|
520
540
|
column_name: '?column?',
|
521
|
-
is_nullable: result.some(col => col.is_nullable),
|
541
|
+
is_nullable: result.columns.some(col => col.is_nullable),
|
522
542
|
table_name: '',
|
523
543
|
table_schema: '',
|
524
|
-
type: result[0].type
|
544
|
+
type: result.columns[0].type
|
525
545
|
};
|
526
546
|
}
|
527
547
|
const a_expr = a_expr_compare.a_expr();
|
@@ -710,7 +730,7 @@ function getNameAndTypeIdFromAExprConst(a_expr_const) {
|
|
710
730
|
if (a_expr_const.NULL_P()) {
|
711
731
|
return {
|
712
732
|
name: a_expr_const.getText(),
|
713
|
-
type: '
|
733
|
+
type: 'null'
|
714
734
|
};
|
715
735
|
}
|
716
736
|
return {
|
@@ -742,12 +762,15 @@ function traversec_expr(c_expr, context, traverseResult) {
|
|
742
762
|
const columnref = c_expr.columnref();
|
743
763
|
if (columnref) {
|
744
764
|
if (context.columnRefIsRecord) {
|
765
|
+
const table = (0, select_columns_1.splitTableName)(columnref.getText());
|
766
|
+
const columns = filterColumns(context.fromColumns, table);
|
745
767
|
return {
|
746
768
|
column_name: columnref.getText(),
|
747
769
|
is_nullable: false,
|
748
770
|
table_name: '',
|
749
771
|
table_schema: '',
|
750
|
-
type: '
|
772
|
+
type: 'record',
|
773
|
+
recordTypes: columns
|
751
774
|
};
|
752
775
|
}
|
753
776
|
else {
|
@@ -782,6 +805,7 @@ function traversec_expr(c_expr, context, traverseResult) {
|
|
782
805
|
}
|
783
806
|
const fun_expr = c_expr.func_expr();
|
784
807
|
if (fun_expr) {
|
808
|
+
context.columnRefIsRecord = undefined;
|
785
809
|
const func_application = fun_expr.func_application();
|
786
810
|
if (func_application) {
|
787
811
|
if (is_json_build_object_func(func_application)) {
|
@@ -807,14 +831,16 @@ function traversec_expr(c_expr, context, traverseResult) {
|
|
807
831
|
}
|
808
832
|
const select_with_parens = c_expr.select_with_parens();
|
809
833
|
if (select_with_parens) {
|
810
|
-
const result = traverse_select_with_parens(select_with_parens, context, traverseResult);
|
834
|
+
const result = traverse_select_with_parens(select_with_parens, Object.assign(Object.assign({}, context), { parentColumns: context.fromColumns, fromColumns: [] }), traverseResult);
|
835
|
+
const jsonType = result.columns[0].jsonType;
|
836
|
+
const is_nullable = jsonType == null ? true : jsonType.name !== 'json[]' && jsonType.name !== 'json_map'; //json[], json_map are not nullable
|
811
837
|
return {
|
812
838
|
column_name: '?column?',
|
813
|
-
is_nullable
|
839
|
+
is_nullable,
|
814
840
|
table_name: '',
|
815
841
|
table_schema: '',
|
816
|
-
type: result[0].type,
|
817
|
-
jsonType:
|
842
|
+
type: result.columns[0].type,
|
843
|
+
jsonType: jsonType != null && jsonType.name === 'json' ? Object.assign(Object.assign({}, jsonType), { notNull: !is_nullable }) : jsonType
|
818
844
|
};
|
819
845
|
}
|
820
846
|
const a_expr_in_parens = c_expr._a_expr_in_parens;
|
@@ -830,8 +856,8 @@ function traversec_expr(c_expr, context, traverseResult) {
|
|
830
856
|
is_nullable: expr_list.some(col => col.is_nullable),
|
831
857
|
table_name: '',
|
832
858
|
table_schema: '',
|
833
|
-
type: '
|
834
|
-
recordTypes: expr_list.map(
|
859
|
+
type: 'record',
|
860
|
+
recordTypes: expr_list.map(expr => (Object.assign(Object.assign({}, expr), { column_name: '' })))
|
835
861
|
};
|
836
862
|
}
|
837
863
|
const implicit_row = c_expr.implicit_row();
|
@@ -867,7 +893,7 @@ function traversec_expr(c_expr, context, traverseResult) {
|
|
867
893
|
function filterColumns(fromColumns, fieldName) {
|
868
894
|
return fromColumns.filter(col => (fieldName.prefix === '' || col.table_name === fieldName.prefix)
|
869
895
|
&& (fieldName.name === '*' || col.column_name === fieldName.name)).map(col => {
|
870
|
-
const result = Object.assign({ column_name: col.column_name, is_nullable: col.is_nullable, table_name: col.table_name, table_schema: col.table_schema, type: col.type }, (col.original_is_nullable !== undefined && { original_is_nullable: col.original_is_nullable }));
|
896
|
+
const result = Object.assign(Object.assign(Object.assign({ column_name: col.column_name, is_nullable: col.is_nullable, table_name: col.table_name, table_schema: col.table_schema, type: col.type }, (col.column_key !== undefined) && { column_key: col.column_key }), (col.jsonType !== undefined) && { jsonType: col.jsonType }), (col.original_is_nullable !== undefined && { original_is_nullable: col.original_is_nullable }));
|
871
897
|
return result;
|
872
898
|
});
|
873
899
|
}
|
@@ -879,36 +905,62 @@ function excludeColumns(fromColumns, excludeList) {
|
|
879
905
|
});
|
880
906
|
}
|
881
907
|
function traversec_expr_case(c_expr_case, context, traverseResult) {
|
882
|
-
var _a
|
908
|
+
var _a;
|
883
909
|
const case_expr = c_expr_case.case_expr();
|
884
|
-
const
|
885
|
-
const
|
886
|
-
const
|
887
|
-
const
|
910
|
+
const whenClauseList = case_expr.when_clause_list().when_clause_list();
|
911
|
+
const whenResult = whenClauseList.map(when_clause => traversewhen_clause(when_clause, context, traverseResult));
|
912
|
+
const whenIsNotNull = whenResult.every(when => !when.is_nullable);
|
913
|
+
const elseExpr = case_expr.case_default();
|
914
|
+
const elseResult = elseExpr ? traverse_a_expr(elseExpr.a_expr(), Object.assign({}, context), traverseResult) : null;
|
915
|
+
const branchNotNull = elseResult ? evaluateBranches(whenClauseList, elseExpr, whenResult.map(col => !col.is_nullable)) : false;
|
916
|
+
const elseIsNotNull = (elseResult === null || elseResult === void 0 ? void 0 : elseResult.is_nullable) === false || branchNotNull;
|
888
917
|
const notNull = elseIsNotNull && whenIsNotNull;
|
889
918
|
return {
|
890
919
|
column_name: '?column?',
|
891
920
|
is_nullable: !notNull,
|
892
921
|
table_name: '',
|
893
922
|
table_schema: '',
|
894
|
-
type: (
|
923
|
+
type: (_a = whenResult[0].type) !== null && _a !== void 0 ? _a : 'unknown',
|
924
|
+
jsonType: allJsonTypesMatch(whenResult, elseResult) ? whenResult[0].jsonType : undefined
|
895
925
|
};
|
896
926
|
}
|
927
|
+
function evaluateBranches(whenClauseList, elseExpr, whenIsNotNull) {
|
928
|
+
const exprIndex = whenClauseList.findIndex(when => {
|
929
|
+
const whenExpr = when.a_expr(0);
|
930
|
+
const evaluatesIfNull = (0, case_nullability_checker_1.evaluatesTrueIfNull)(elseExpr, whenExpr);
|
931
|
+
if (evaluatesIfNull) {
|
932
|
+
return true;
|
933
|
+
}
|
934
|
+
return false;
|
935
|
+
});
|
936
|
+
const result = exprIndex !== -1 ? whenIsNotNull[exprIndex] : false;
|
937
|
+
return result;
|
938
|
+
}
|
939
|
+
function allJsonTypesMatch(whenResultList, elseResult) {
|
940
|
+
var _a;
|
941
|
+
const firstType = (_a = whenResultList[0]) === null || _a === void 0 ? void 0 : _a.jsonType;
|
942
|
+
const allMatch = whenResultList.every(res => {
|
943
|
+
const match = res.jsonType == firstType;
|
944
|
+
return match;
|
945
|
+
}) && ((elseResult === null || elseResult === void 0 ? void 0 : elseResult.type) === 'null' || (elseResult === null || elseResult === void 0 ? void 0 : elseResult.jsonType) == firstType);
|
946
|
+
return allMatch;
|
947
|
+
}
|
897
948
|
function traversewhen_clause(when_clause, context, traverseResult) {
|
898
949
|
const a_expr_list = when_clause.a_expr_list();
|
899
950
|
const [whenExprList, thenExprList] = partition(a_expr_list, (index) => index % 2 === 0);
|
900
951
|
const whenExprResult = thenExprList.map((thenExpr, index) => {
|
901
952
|
traverse_a_expr(whenExprList[index], context, traverseResult);
|
902
|
-
const thenExprResult = traverse_a_expr(thenExpr, context, traverseResult);
|
953
|
+
const thenExprResult = traverse_a_expr(thenExpr, Object.assign(Object.assign({}, context), { filter_expr: whenExprList[index] }), traverseResult);
|
903
954
|
return thenExprResult;
|
904
955
|
});
|
905
|
-
const notNull = whenExprResult.every(res => res);
|
956
|
+
const notNull = whenExprResult.every(res => !res.is_nullable);
|
906
957
|
return {
|
907
958
|
column_name: '?column?',
|
908
959
|
is_nullable: !notNull,
|
909
960
|
table_name: '',
|
910
961
|
table_schema: '',
|
911
|
-
type: whenExprResult[0].type
|
962
|
+
type: whenExprResult[0].type,
|
963
|
+
jsonType: whenExprResult[0].jsonType
|
912
964
|
};
|
913
965
|
}
|
914
966
|
function partition(array, predicate) {
|
@@ -952,33 +1004,31 @@ function mapJsonBuildArgsToJsonProperty(args, filterExpr) {
|
|
952
1004
|
return properties;
|
953
1005
|
}
|
954
1006
|
function inferJsonNullability(columns, filterExpr) {
|
955
|
-
const
|
1007
|
+
const tables = columns.filter(col => filterExpr && col.original_is_nullable === false
|
1008
|
+
&& isNotNull_a_expr({ name: col.column_name, prefix: col.table_name }, filterExpr))
|
1009
|
+
.map(col => col.table_name);
|
956
1010
|
const fields = columns.map(col => {
|
957
|
-
|
958
|
-
return col.original_is_nullable != null ? !col.original_is_nullable : Boolean(filterExpr && isNotNull_a_expr(col, filterExpr));
|
959
|
-
}
|
960
|
-
else {
|
961
|
-
return !col.is_nullable || Boolean(filterExpr && isNotNull_a_expr(col, filterExpr));
|
962
|
-
}
|
1011
|
+
return col.original_is_nullable != null && tables.includes(col.table_name) ? !col.original_is_nullable : !col.is_nullable;
|
963
1012
|
});
|
964
1013
|
return fields;
|
965
1014
|
}
|
966
1015
|
function transformFieldsToJsonObjType(fields) {
|
967
1016
|
const jsonObject = {
|
968
1017
|
name: 'json',
|
1018
|
+
notNull: true,
|
969
1019
|
properties: fields.map(col => mapFieldToPropertyDef(col))
|
970
1020
|
};
|
971
1021
|
return jsonObject;
|
972
1022
|
}
|
973
1023
|
function mapFieldToPropertyDef(field) {
|
974
1024
|
const prop = {
|
975
|
-
key: field.
|
1025
|
+
key: field.column_name,
|
976
1026
|
type: transformFieldToJsonField(field)
|
977
1027
|
};
|
978
1028
|
return prop;
|
979
1029
|
}
|
980
1030
|
function transformFieldToJsonField(field) {
|
981
|
-
const jsonField = { name: 'json_field', type: field.type, notNull: field.
|
1031
|
+
const jsonField = field.jsonType ? field.jsonType : { name: 'json_field', type: field.type, notNull: !field.is_nullable };
|
982
1032
|
return jsonField;
|
983
1033
|
}
|
984
1034
|
function traverse_json_build_obj_func(func_application, context, traverseResult) {
|
@@ -986,36 +1036,54 @@ function traverse_json_build_obj_func(func_application, context, traverseResult)
|
|
986
1036
|
const columnName = ((_a = func_application.func_name()) === null || _a === void 0 ? void 0 : _a.getText()) || func_application.getText();
|
987
1037
|
const func_arg_expr_list = ((_b = func_application.func_arg_list()) === null || _b === void 0 ? void 0 : _b.func_arg_expr_list()) || [];
|
988
1038
|
const argsResult = func_arg_expr_list.map(func_arg_expr => traversefunc_arg_expr(func_arg_expr, context, traverseResult));
|
989
|
-
|
1039
|
+
const result = {
|
990
1040
|
column_name: columnName,
|
991
|
-
is_nullable:
|
1041
|
+
is_nullable: false,
|
992
1042
|
table_name: '',
|
993
1043
|
table_schema: '',
|
994
1044
|
type: 'json',
|
995
1045
|
jsonType: {
|
996
1046
|
name: 'json',
|
1047
|
+
notNull: true,
|
997
1048
|
properties: mapJsonBuildArgsToJsonProperty(argsResult, context.filter_expr),
|
998
1049
|
}
|
999
1050
|
};
|
1051
|
+
return result;
|
1000
1052
|
}
|
1001
1053
|
function traverse_json_agg(func_application, context, traverseResult) {
|
1002
1054
|
var _a, _b;
|
1003
1055
|
const columnName = ((_a = func_application.func_name()) === null || _a === void 0 ? void 0 : _a.getText()) || func_application.getText();
|
1004
1056
|
const func_arg_expr_list = ((_b = func_application.func_arg_list()) === null || _b === void 0 ? void 0 : _b.func_arg_expr_list()) || [];
|
1005
|
-
const argsResult = func_arg_expr_list.map(func_arg_expr => traversefunc_arg_expr(func_arg_expr, context, traverseResult));
|
1057
|
+
const argsResult = func_arg_expr_list.map(func_arg_expr => traversefunc_arg_expr(func_arg_expr, Object.assign(Object.assign({}, context), { columnRefIsRecord: true }), traverseResult));
|
1006
1058
|
const result = {
|
1007
1059
|
column_name: columnName,
|
1008
1060
|
is_nullable: context.filter_expr != null,
|
1009
1061
|
table_name: '',
|
1010
1062
|
table_schema: '',
|
1011
1063
|
type: 'json[]',
|
1012
|
-
jsonType:
|
1013
|
-
name: 'json[]',
|
1014
|
-
properties: argsResult.map(arg => arg.jsonType || { name: 'json_field', type: arg.type, notNull: !arg.is_nullable })
|
1015
|
-
}
|
1064
|
+
jsonType: createJsonTypeForJsonAgg(argsResult[0], context.filter_expr)
|
1016
1065
|
};
|
1017
1066
|
return result;
|
1018
1067
|
}
|
1068
|
+
function createJsonTypeForJsonAgg(arg, filter_expr) {
|
1069
|
+
if (arg.recordTypes) {
|
1070
|
+
const jsonType = mapRecordsToJsonType(arg.recordTypes, filter_expr);
|
1071
|
+
return {
|
1072
|
+
name: 'json[]',
|
1073
|
+
properties: [jsonType]
|
1074
|
+
};
|
1075
|
+
}
|
1076
|
+
const jsonType = arg.jsonType ? { name: 'json[]', properties: [arg.jsonType] } : undefined;
|
1077
|
+
const fieldType = { name: 'json[]', properties: [{ name: 'json_field', type: arg.type, notNull: !arg.is_nullable }] };
|
1078
|
+
const result = jsonType || fieldType;
|
1079
|
+
return result;
|
1080
|
+
}
|
1081
|
+
function mapRecordsToJsonType(recordTypes, filterExpr) {
|
1082
|
+
const jsonNullability = inferJsonNullability(recordTypes, filterExpr);
|
1083
|
+
const fields = recordTypes.map((col, index) => (Object.assign(Object.assign({}, col), { column_name: col.column_name ? col.column_name : `f${index + 1}`, is_nullable: !jsonNullability[index] })));
|
1084
|
+
const jsonType = transformFieldsToJsonObjType(fields);
|
1085
|
+
return jsonType;
|
1086
|
+
}
|
1019
1087
|
function traversefunc_application(func_application, context, traverseResult) {
|
1020
1088
|
var _a;
|
1021
1089
|
const functionName = getFunctionName(func_application);
|
@@ -1023,22 +1091,7 @@ function traversefunc_application(func_application, context, traverseResult) {
|
|
1023
1091
|
if (functionName === 'row_to_json') {
|
1024
1092
|
const argResult = traversefunc_arg_expr(func_arg_expr_list[0], Object.assign(Object.assign({}, context), { columnRefIsRecord: true }), traverseResult);
|
1025
1093
|
if (argResult.recordTypes) {
|
1026
|
-
const
|
1027
|
-
const jsonType = transformFieldsToJsonObjType(fields);
|
1028
|
-
return {
|
1029
|
-
column_name: functionName,
|
1030
|
-
is_nullable: false,
|
1031
|
-
table_name: '',
|
1032
|
-
table_schema: '',
|
1033
|
-
type: 'json',
|
1034
|
-
jsonType
|
1035
|
-
};
|
1036
|
-
}
|
1037
|
-
else {
|
1038
|
-
const columns = filterColumns(context.fromColumns, { name: '*', prefix: argResult.column_name });
|
1039
|
-
const jsonNullability = inferJsonNullability(columns, context.filter_expr);
|
1040
|
-
const fields = columns.map((col, index) => ({ name: col.column_name, type: col.type, notNull: jsonNullability[index] }));
|
1041
|
-
const jsonType = transformFieldsToJsonObjType(fields);
|
1094
|
+
const jsonType = mapRecordsToJsonType(argResult.recordTypes, context.filter_expr);
|
1042
1095
|
return {
|
1043
1096
|
column_name: functionName,
|
1044
1097
|
is_nullable: false,
|
@@ -1170,6 +1223,28 @@ function traversefunc_application(func_application, context, traverseResult) {
|
|
1170
1223
|
if (functionName === 'sum') {
|
1171
1224
|
return Object.assign(Object.assign({}, argsResult[0]), { column_name: functionName, is_nullable: true, type: argsResult[0].type ? mapSumType(argsResult[0].type) : 'unknown' });
|
1172
1225
|
}
|
1226
|
+
if (functionName === 'json_object_agg') {
|
1227
|
+
return {
|
1228
|
+
column_name: functionName,
|
1229
|
+
is_nullable: false,
|
1230
|
+
table_name: '',
|
1231
|
+
table_schema: '',
|
1232
|
+
type: 'json',
|
1233
|
+
jsonType: {
|
1234
|
+
name: 'json_map',
|
1235
|
+
type: argsResult[1].jsonType || { name: 'json_field', type: argsResult[1].type, notNull: !argsResult[1].is_nullable }
|
1236
|
+
}
|
1237
|
+
};
|
1238
|
+
}
|
1239
|
+
if (functionName === 'unnest') {
|
1240
|
+
return {
|
1241
|
+
column_name: functionName,
|
1242
|
+
is_nullable: argsResult[0].is_nullable,
|
1243
|
+
type: argsResult[0].type,
|
1244
|
+
table_name: '',
|
1245
|
+
table_schema: ''
|
1246
|
+
};
|
1247
|
+
}
|
1173
1248
|
return {
|
1174
1249
|
column_name: functionName,
|
1175
1250
|
is_nullable: true,
|
@@ -1229,7 +1304,7 @@ function traverse_array_expr(array_expr, context, traverseResult) {
|
|
1229
1304
|
const expr_list = array_expr.expr_list();
|
1230
1305
|
if (expr_list) {
|
1231
1306
|
const traverse_expr_list_result = traverse_expr_list(expr_list, context, traverseResult);
|
1232
|
-
return Object.assign(Object.assign({}, traverse_expr_list_result[0]), { column_name: '?column?', table_name: '', table_schema: '' });
|
1307
|
+
return Object.assign(Object.assign({}, traverse_expr_list_result[0]), { is_nullable: traverse_expr_list_result.some(expr => expr.is_nullable), column_name: '?column?', table_name: '', table_schema: '' });
|
1233
1308
|
}
|
1234
1309
|
const array_expr_list = array_expr.array_expr_list();
|
1235
1310
|
if (array_expr_list) {
|
@@ -1251,44 +1326,92 @@ function traverse_array_expr_list(array_expr_list, context, traverseResult) {
|
|
1251
1326
|
return result;
|
1252
1327
|
}
|
1253
1328
|
function findColumn(fieldName, fromColumns) {
|
1254
|
-
const col =
|
1329
|
+
const col = findColumnOrNull(fieldName, fromColumns);
|
1255
1330
|
if (col == null) {
|
1256
1331
|
throw Error('Column not found: ' + fieldNameToString(fieldName));
|
1257
1332
|
}
|
1258
1333
|
return col;
|
1259
1334
|
}
|
1335
|
+
function findColumnOrNull(fieldName, fromColumns) {
|
1336
|
+
const col = fromColumns.find(col => (fieldName.prefix === '' || col.table_name.toLowerCase() === fieldName.prefix.toLowerCase()) && col.column_name.toLowerCase() === fieldName.name.toLowerCase());
|
1337
|
+
return col;
|
1338
|
+
}
|
1260
1339
|
function fieldNameToString(fieldName) {
|
1261
1340
|
return fieldName.prefix !== '' ? `${fieldName.prefix}.${fieldName.name}` : fieldName.name;
|
1262
1341
|
}
|
1263
1342
|
function checkIsNullable(where_clause, field) {
|
1264
|
-
const isNotNullResult = !field.is_nullable || isNotNull(field, where_clause);
|
1343
|
+
const isNotNullResult = !field.is_nullable || isNotNull({ name: field.column_name, prefix: field.table_name }, where_clause);
|
1265
1344
|
const col = Object.assign(Object.assign({}, field), { is_nullable: !isNotNullResult });
|
1266
1345
|
return col;
|
1267
1346
|
}
|
1268
1347
|
function traverse_from_clause(from_clause, context, traverseResult) {
|
1269
1348
|
const from_list = from_clause.from_list();
|
1270
1349
|
if (from_list) {
|
1271
|
-
|
1350
|
+
const fromListResult = traverse_from_list(from_list, context, traverseResult);
|
1351
|
+
return fromListResult;
|
1272
1352
|
}
|
1273
|
-
return
|
1353
|
+
return {
|
1354
|
+
columns: [],
|
1355
|
+
singleRow: false
|
1356
|
+
};
|
1274
1357
|
}
|
1275
1358
|
function traverse_from_list(from_list, context, traverseResult) {
|
1276
|
-
const
|
1277
|
-
|
1359
|
+
const fromListResult = from_list.table_ref_list().map(table_ref => traverse_table_ref(table_ref, context, traverseResult));
|
1360
|
+
const columns = fromListResult.flatMap(tableRes => tableRes.columns);
|
1361
|
+
return {
|
1362
|
+
columns: columns,
|
1363
|
+
singleRow: fromListResult.length === 1 ? fromListResult[0].singleRow : false
|
1364
|
+
};
|
1365
|
+
}
|
1366
|
+
function getFromColumns(tableName, withColumns, dbSchema) {
|
1367
|
+
const filteredWithColumns = withColumns.filter(col => col.table_name.toLowerCase() === tableName.name.toLowerCase());
|
1368
|
+
const filteredSchema = filteredWithColumns.length > 0 ? filteredWithColumns : dbSchema.filter(col => col.table_name.toLowerCase() === tableName.name.toLowerCase());
|
1369
|
+
return filteredSchema;
|
1370
|
+
}
|
1371
|
+
function checkLeftJoinIsNullable(leftJoin, subsequentJoins, fromColumns) {
|
1372
|
+
if (!leftJoin.joinQual) {
|
1373
|
+
return true; // No condition = always nullable
|
1374
|
+
}
|
1375
|
+
const leftTable = getTableName(leftJoin.tableRef);
|
1376
|
+
const leftJoinColumns = getJoinColumns(leftJoin.joinQual)
|
1377
|
+
.filter(col => {
|
1378
|
+
if (col.prefix === leftTable.name && col.prefix === leftTable.alias) {
|
1379
|
+
return true;
|
1380
|
+
}
|
1381
|
+
if (findColumnOrNull(col, fromColumns)) { //column without table reference (ex: fk_user)
|
1382
|
+
return true;
|
1383
|
+
}
|
1384
|
+
return false;
|
1385
|
+
});
|
1386
|
+
for (const join of subsequentJoins) {
|
1387
|
+
// Only INNER JOINs can cancel nullability
|
1388
|
+
if (!(join.joinType === null || join.joinType.INNER_P())) {
|
1389
|
+
continue;
|
1390
|
+
}
|
1391
|
+
const joinConditionColumns = getJoinColumns(join.joinQual);
|
1392
|
+
const filteredJoinColumnsFromLeft = joinConditionColumns.filter(col => leftJoinColumns.some(lc => lc.prefix === col.prefix));
|
1393
|
+
// Are *all* columns from the left join used in not-null filtering?
|
1394
|
+
const referencesLeftJoin = filteredJoinColumnsFromLeft.length > 0 &&
|
1395
|
+
filteredJoinColumnsFromLeft.every(col => { var _a; return isNotNull_a_expr(col, (_a = join.joinQual) === null || _a === void 0 ? void 0 : _a.a_expr()); });
|
1396
|
+
if (referencesLeftJoin) {
|
1397
|
+
return false; // LEFT JOIN is effectively filtered by INNER JOIN — not nullable
|
1398
|
+
}
|
1399
|
+
}
|
1400
|
+
return true; // No INNER JOIN filtered it — remains nullable
|
1278
1401
|
}
|
1279
1402
|
function traverse_table_ref(table_ref, context, traverseResult) {
|
1280
|
-
var _a, _b, _c, _d;
|
1281
|
-
const { fromColumns, dbSchema } = context;
|
1403
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
1282
1404
|
const allColumns = [];
|
1283
1405
|
const relation_expr = table_ref.relation_expr();
|
1284
1406
|
const aliasClause = table_ref.alias_clause();
|
1285
1407
|
const aliasNameList = (_b = (_a = aliasClause === null || aliasClause === void 0 ? void 0 : aliasClause.name_list()) === null || _a === void 0 ? void 0 : _a.name_list()) === null || _b === void 0 ? void 0 : _b.map(name => name.getText());
|
1286
1408
|
const alias = aliasClause ? aliasClause.colid().getText() : '';
|
1287
1409
|
if (relation_expr) {
|
1288
|
-
const tableName = traverse_relation_expr(relation_expr
|
1410
|
+
const tableName = traverse_relation_expr(relation_expr);
|
1411
|
+
const fromColumns = getFromColumns(tableName, context.withColumns, context.dbSchema);
|
1289
1412
|
const tableNameWithAlias = alias ? alias : tableName.name;
|
1290
|
-
const
|
1291
|
-
|
1413
|
+
const columnsWithAlias = fromColumns.map(col => (Object.assign(Object.assign({}, col), { table_name: tableNameWithAlias.toLowerCase() })));
|
1414
|
+
const fromColumnsResult = columnsWithAlias.concat(context.parentColumns);
|
1292
1415
|
allColumns.push(...fromColumnsResult);
|
1293
1416
|
if (context.collectNestedInfo) {
|
1294
1417
|
const key = fromColumnsResult.filter(col => col.column_key === 'PRI');
|
@@ -1304,47 +1427,79 @@ function traverse_table_ref(table_ref, context, traverseResult) {
|
|
1304
1427
|
};
|
1305
1428
|
(_d = traverseResult.relations) === null || _d === void 0 ? void 0 : _d.push(relation);
|
1306
1429
|
}
|
1307
|
-
const table_ref_list = table_ref.table_ref_list();
|
1308
|
-
const join_type_list = table_ref.join_type_list();
|
1309
|
-
const join_qual_list = table_ref.join_qual_list();
|
1310
1430
|
if (context.collectDynamicQueryInfo && traverseResult.dynamicQueryInfo.from.length == 0) {
|
1311
1431
|
collectDynamicQueryInfoTableRef(table_ref, null, null, fromColumnsResult, [], traverseResult);
|
1312
1432
|
}
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
}
|
1332
|
-
|
1333
|
-
|
1433
|
+
const joinList = extractJoins(table_ref);
|
1434
|
+
const joinColumns = joinList.flatMap((join, index) => {
|
1435
|
+
const joinType = join.joinType; //INNER, LEFT
|
1436
|
+
const joinQual = join.joinQual;
|
1437
|
+
const numParamsBefore = traverseResult.parameters.length;
|
1438
|
+
const parentColumns = join.tableRef.LATERAL_P() ? fromColumnsResult : [];
|
1439
|
+
const joinTableRefResult = traverse_table_ref(join.tableRef, Object.assign(Object.assign({}, context), { parentColumns }), traverseResult);
|
1440
|
+
const isLeftJoin = joinType === null || joinType === void 0 ? void 0 : joinType.LEFT();
|
1441
|
+
const filteredColumns = joinQual && (joinQual === null || joinQual === void 0 ? void 0 : joinQual.USING()) ? filterUsingColumns(joinTableRefResult.columns, joinQual) : joinTableRefResult.columns;
|
1442
|
+
const subsequentJoints = joinList.slice(index + 1);
|
1443
|
+
const checkIsNullable = isLeftJoin ? checkLeftJoinIsNullable(join, subsequentJoints, filteredColumns) : false;
|
1444
|
+
;
|
1445
|
+
const resultColumns = isLeftJoin ? filteredColumns.map(col => {
|
1446
|
+
const colResult = Object.assign(Object.assign({}, col), { is_nullable: checkIsNullable ? true : col.is_nullable, original_is_nullable: col.is_nullable });
|
1447
|
+
return colResult;
|
1448
|
+
}) : filteredColumns;
|
1449
|
+
if (context.collectNestedInfo && joinQual) {
|
1450
|
+
collectNestedInfo(joinQual, resultColumns, traverseResult);
|
1451
|
+
}
|
1452
|
+
if (context.collectDynamicQueryInfo) {
|
1453
|
+
const parameters = traverseResult.parameters.slice(numParamsBefore).map((_, index) => index + numParamsBefore);
|
1454
|
+
collectDynamicQueryInfoTableRef(join.tableRef, joinType, joinQual, resultColumns, parameters, traverseResult);
|
1455
|
+
}
|
1456
|
+
return resultColumns;
|
1457
|
+
});
|
1458
|
+
allColumns.push(...joinColumns);
|
1459
|
+
}
|
1460
|
+
const func_table = table_ref.func_table();
|
1461
|
+
if (func_table) {
|
1462
|
+
const funcAlias = ((_g = (_f = (_e = table_ref.func_alias_clause()) === null || _e === void 0 ? void 0 : _e.alias_clause()) === null || _f === void 0 ? void 0 : _f.colid()) === null || _g === void 0 ? void 0 : _g.getText()) || '';
|
1463
|
+
const result = traverse_func_table(func_table, context, traverseResult);
|
1464
|
+
const resultWithAlias = result.columns.map(col => (Object.assign(Object.assign({}, col), { table_name: funcAlias || col.table_name })));
|
1465
|
+
return {
|
1466
|
+
columns: resultWithAlias,
|
1467
|
+
singleRow: result.singleRow
|
1468
|
+
};
|
1334
1469
|
}
|
1335
1470
|
const select_with_parens = table_ref.select_with_parens();
|
1336
1471
|
if (select_with_parens) {
|
1337
1472
|
const columns = traverse_select_with_parens(select_with_parens, Object.assign(Object.assign({}, context), { collectDynamicQueryInfo: false }), traverseResult);
|
1338
|
-
const withAlias = columns.map((col, i) => ({
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1473
|
+
const withAlias = columns.columns.map((col, i) => (Object.assign(Object.assign({}, col), { column_name: (aliasNameList === null || aliasNameList === void 0 ? void 0 : aliasNameList[i]) || col.column_name, table_name: alias || col.table_name })));
|
1474
|
+
return {
|
1475
|
+
columns: withAlias,
|
1476
|
+
singleRow: false
|
1477
|
+
};
|
1478
|
+
}
|
1479
|
+
return {
|
1480
|
+
columns: allColumns,
|
1481
|
+
singleRow: false
|
1482
|
+
};
|
1483
|
+
}
|
1484
|
+
function extractJoins(table_ref) {
|
1485
|
+
const joinList = [];
|
1486
|
+
let currentJoinType = null;
|
1487
|
+
for (const child of table_ref.children || []) {
|
1488
|
+
if (child instanceof PostgreSQLParser_1.Join_typeContext) {
|
1489
|
+
currentJoinType = child;
|
1490
|
+
}
|
1491
|
+
if (child instanceof PostgreSQLParser_1.Table_refContext) {
|
1492
|
+
joinList.push({ joinQual: null, joinType: currentJoinType, tableRef: child });
|
1493
|
+
currentJoinType = null;
|
1494
|
+
}
|
1495
|
+
if (child instanceof PostgreSQLParser_1.Join_qualContext) {
|
1496
|
+
const lastJoin = joinList.at(-1);
|
1497
|
+
if (lastJoin) {
|
1498
|
+
lastJoin.joinQual = child;
|
1499
|
+
}
|
1500
|
+
}
|
1346
1501
|
}
|
1347
|
-
return
|
1502
|
+
return joinList;
|
1348
1503
|
}
|
1349
1504
|
function collectDynamicQueryInfoTableRef(table_ref, joinType, joinQual, columns, parameters, traverseResult) {
|
1350
1505
|
var _a, _b;
|
@@ -1414,16 +1569,88 @@ function traverse_select_with_parens(select_with_parens, context, traverseResult
|
|
1414
1569
|
if (select_no_parens) {
|
1415
1570
|
return traverse_select_no_parens(select_no_parens, context, traverseResult);
|
1416
1571
|
}
|
1417
|
-
return
|
1572
|
+
return {
|
1573
|
+
columns: [],
|
1574
|
+
singleRow: false
|
1575
|
+
};
|
1576
|
+
}
|
1577
|
+
function traverse_func_table(func_table, context, traverseResult) {
|
1578
|
+
const func_expr_windowless = func_table.func_expr_windowless();
|
1579
|
+
if (func_expr_windowless) {
|
1580
|
+
const result = traverse_func_expr_windowless(func_expr_windowless, context, traverseResult);
|
1581
|
+
return result;
|
1582
|
+
}
|
1583
|
+
throw Error('Stmt not supported: ' + func_table.getText());
|
1584
|
+
}
|
1585
|
+
function parseReturnType(returnType) {
|
1586
|
+
const trimmed = returnType.trim();
|
1587
|
+
// Handle TABLE(...)
|
1588
|
+
if (trimmed.toLowerCase().startsWith('table(') && trimmed.endsWith(')')) {
|
1589
|
+
const inside = trimmed.slice(6, -1); // remove "TABLE(" and final ")"
|
1590
|
+
const columns = [];
|
1591
|
+
const parts = inside.split(',').map(part => part.trim());
|
1592
|
+
for (const part of parts) {
|
1593
|
+
const match = part.match(/^(\w+)\s+(.+)$/);
|
1594
|
+
if (!match) {
|
1595
|
+
throw new Error(`Invalid column definition: ${part}`);
|
1596
|
+
}
|
1597
|
+
const [, name, type] = match;
|
1598
|
+
columns.push({ name, type });
|
1599
|
+
}
|
1600
|
+
return { kind: 'table', columns };
|
1601
|
+
}
|
1602
|
+
// Handle SETOF typename
|
1603
|
+
const setofMatch = trimmed.match(/^SETOF\s+(\w+)$/i);
|
1604
|
+
if (setofMatch) {
|
1605
|
+
return { kind: 'setof', table: setofMatch[1] };
|
1606
|
+
}
|
1607
|
+
throw new Error(`Unsupported return type format: ${returnType}`);
|
1608
|
+
}
|
1609
|
+
function traverse_func_expr_windowless(func_expr_windowless, context, traverseResult) {
|
1610
|
+
const func_application = func_expr_windowless.func_application();
|
1611
|
+
if (func_application) {
|
1612
|
+
const func_name = func_application.func_name().getText().toLowerCase();
|
1613
|
+
const funcSchema = context.userFunctions.find(func => func.function_name.toLowerCase() === func_name);
|
1614
|
+
if (funcSchema) {
|
1615
|
+
const definition = funcSchema.definition;
|
1616
|
+
const returnType = parseReturnType(funcSchema.return_type);
|
1617
|
+
const functionColumns = returnType.kind === 'table' ? returnType.columns.map(col => {
|
1618
|
+
const columnInfo = {
|
1619
|
+
column_name: col.name,
|
1620
|
+
type: col.type,
|
1621
|
+
is_nullable: true,
|
1622
|
+
table_name: '',
|
1623
|
+
table_schema: ''
|
1624
|
+
};
|
1625
|
+
return columnInfo;
|
1626
|
+
}) : context.dbSchema.filter(col => col.table_name.toLowerCase() === returnType.table);
|
1627
|
+
if (funcSchema.language.toLowerCase() === 'sql') {
|
1628
|
+
const parser = (0, postgres_1.parseSql)(definition);
|
1629
|
+
const selectstmt = parser.stmt().selectstmt();
|
1630
|
+
const { columns, multipleRowsResult } = traverseSelectstmt(selectstmt, context, traverseResult);
|
1631
|
+
return {
|
1632
|
+
columns: columns.map((c) => (Object.assign(Object.assign({}, c), { table_name: funcSchema.function_name }))),
|
1633
|
+
singleRow: !multipleRowsResult
|
1634
|
+
};
|
1635
|
+
}
|
1636
|
+
else {
|
1637
|
+
return {
|
1638
|
+
columns: functionColumns,
|
1639
|
+
singleRow: false
|
1640
|
+
};
|
1641
|
+
}
|
1642
|
+
}
|
1643
|
+
}
|
1644
|
+
throw Error('Stmt not supported: ' + func_expr_windowless.getText());
|
1418
1645
|
}
|
1419
|
-
function traverse_relation_expr(relation_expr
|
1646
|
+
function traverse_relation_expr(relation_expr) {
|
1420
1647
|
const qualified_name = relation_expr.qualified_name();
|
1421
|
-
const name = traverse_qualified_name(qualified_name
|
1648
|
+
const name = traverse_qualified_name(qualified_name);
|
1422
1649
|
return name;
|
1423
1650
|
}
|
1424
|
-
function traverse_qualified_name(qualified_name
|
1651
|
+
function traverse_qualified_name(qualified_name) {
|
1425
1652
|
var _a, _b;
|
1426
|
-
const colid_name = qualified_name.colid() ? get_colid_text(qualified_name.colid()
|
1653
|
+
const colid_name = qualified_name.colid() ? get_colid_text(qualified_name.colid()) : '';
|
1427
1654
|
const indirection_el_list = (_a = qualified_name.indirection()) === null || _a === void 0 ? void 0 : _a.indirection_el_list();
|
1428
1655
|
if (indirection_el_list && indirection_el_list.length === 1) {
|
1429
1656
|
return {
|
@@ -1436,22 +1663,22 @@ function traverse_qualified_name(qualified_name, dbSchema) {
|
|
1436
1663
|
alias: ''
|
1437
1664
|
};
|
1438
1665
|
}
|
1439
|
-
function get_colid_text(colid
|
1666
|
+
function get_colid_text(colid) {
|
1440
1667
|
const identifier = colid.identifier();
|
1441
1668
|
if (identifier) {
|
1442
|
-
return traverse_identifier(identifier
|
1669
|
+
return traverse_identifier(identifier);
|
1443
1670
|
}
|
1444
1671
|
const unreserved_keyword = colid.unreserved_keyword();
|
1445
1672
|
if (unreserved_keyword) {
|
1446
|
-
return traverse_unreserved_keyword(unreserved_keyword
|
1673
|
+
return traverse_unreserved_keyword(unreserved_keyword);
|
1447
1674
|
}
|
1448
1675
|
return '';
|
1449
1676
|
}
|
1450
|
-
function traverse_identifier(identifier
|
1677
|
+
function traverse_identifier(identifier) {
|
1451
1678
|
const tableName = identifier.Identifier().getText();
|
1452
1679
|
return tableName;
|
1453
1680
|
}
|
1454
|
-
function traverse_unreserved_keyword(unreserved_keyword
|
1681
|
+
function traverse_unreserved_keyword(unreserved_keyword) {
|
1455
1682
|
return unreserved_keyword.getText();
|
1456
1683
|
}
|
1457
1684
|
function paramIsList(c_expr) {
|
@@ -1486,8 +1713,7 @@ function paramIsList(c_expr) {
|
|
1486
1713
|
function traverseInsertstmt(insertstmt, dbSchema) {
|
1487
1714
|
const traverseResult = {
|
1488
1715
|
columnsNullability: [],
|
1489
|
-
parameters: []
|
1490
|
-
singleRow: false
|
1716
|
+
parameters: []
|
1491
1717
|
};
|
1492
1718
|
const insert_target = insertstmt.insert_target();
|
1493
1719
|
const tableName = insert_target.getText();
|
@@ -1499,7 +1725,10 @@ function traverseInsertstmt(insertstmt, dbSchema) {
|
|
1499
1725
|
const context = {
|
1500
1726
|
dbSchema,
|
1501
1727
|
fromColumns: insertColumns,
|
1728
|
+
parentColumns: [],
|
1729
|
+
withColumns: [],
|
1502
1730
|
checkConstraints: {},
|
1731
|
+
userFunctions: [],
|
1503
1732
|
collectNestedInfo: false,
|
1504
1733
|
collectDynamicQueryInfo: false
|
1505
1734
|
};
|
@@ -1535,7 +1764,10 @@ function traverseDeletestmt(deleteStmt, dbSchema, traverseResult) {
|
|
1535
1764
|
const context = {
|
1536
1765
|
dbSchema,
|
1537
1766
|
fromColumns: deleteColumns,
|
1767
|
+
parentColumns: [],
|
1768
|
+
withColumns: [],
|
1538
1769
|
checkConstraints: {},
|
1770
|
+
userFunctions: [],
|
1539
1771
|
collectNestedInfo: false,
|
1540
1772
|
collectDynamicQueryInfo: false
|
1541
1773
|
};
|
@@ -1557,7 +1789,7 @@ function traverseDeletestmt(deleteStmt, dbSchema, traverseResult) {
|
|
1557
1789
|
return result;
|
1558
1790
|
}
|
1559
1791
|
function addConstraintIfNotNull(checkConstraint) {
|
1560
|
-
return checkConstraint !== undefined ? { checkConstraint } :
|
1792
|
+
return checkConstraint !== undefined ? { checkConstraint } : undefined;
|
1561
1793
|
}
|
1562
1794
|
function traverseUpdatestmt(updatestmt, traverseContext, traverseResult) {
|
1563
1795
|
var _a;
|
@@ -1570,12 +1802,12 @@ function traverseUpdatestmt(updatestmt, traverseContext, traverseResult) {
|
|
1570
1802
|
updatestmt.set_clause_list().set_clause_list()
|
1571
1803
|
.forEach(set_clause => traverse_set_clause(set_clause, context, traverseResult));
|
1572
1804
|
const from_clause = updatestmt.from_clause();
|
1573
|
-
const
|
1805
|
+
const fromResult = from_clause ? traverse_from_clause(from_clause, context, traverseResult) : { columns: [], singleRow: true };
|
1574
1806
|
const parametersBefore = traverseResult.parameters.length;
|
1575
1807
|
const where_clause = updatestmt.where_or_current_clause();
|
1576
1808
|
if (where_clause) {
|
1577
1809
|
const a_expr = where_clause.a_expr();
|
1578
|
-
traverse_a_expr(a_expr, Object.assign(Object.assign({}, context), { fromColumns: updateColumns.concat(
|
1810
|
+
traverse_a_expr(a_expr, Object.assign(Object.assign({}, context), { fromColumns: updateColumns.concat(fromResult.columns) }), traverseResult);
|
1579
1811
|
}
|
1580
1812
|
const whereParameters = traverseResult.parameters.slice(parametersBefore);
|
1581
1813
|
const returning_clause = updatestmt.returning_clause();
|
@@ -1690,7 +1922,14 @@ function isNotNull_a_expr_unary_not(a_expr_unary_not, field) {
|
|
1690
1922
|
function isNotNull_a_expr_isnull(a_expr_isnull, field) {
|
1691
1923
|
const a_expr_is_not = a_expr_isnull.a_expr_is_not();
|
1692
1924
|
if (a_expr_is_not) {
|
1693
|
-
|
1925
|
+
const isNotNull = isNotNull_a_expr_is_not(a_expr_is_not, field);
|
1926
|
+
if (isNotNull && a_expr_is_not.IS() && a_expr_is_not.NOT() && a_expr_is_not.NULL_P()) {
|
1927
|
+
return true;
|
1928
|
+
}
|
1929
|
+
if (a_expr_is_not.IS() && a_expr_is_not.NULL_P()) {
|
1930
|
+
return false;
|
1931
|
+
}
|
1932
|
+
return isNotNull;
|
1694
1933
|
}
|
1695
1934
|
return false;
|
1696
1935
|
}
|
@@ -1814,7 +2053,7 @@ function isNotNull_c_expr(c_expr, field) {
|
|
1814
2053
|
const columnref = c_expr.columnref();
|
1815
2054
|
if (columnref) {
|
1816
2055
|
const fieldName = (0, select_columns_1.splitName)(columnref.getText());
|
1817
|
-
return (fieldName.name === field.
|
2056
|
+
return (fieldName.name === field.name && (fieldName.prefix === '' || field.prefix === fieldName.prefix));
|
1818
2057
|
}
|
1819
2058
|
const aexprconst = c_expr.aexprconst();
|
1820
2059
|
if (aexprconst) {
|
@@ -1840,8 +2079,8 @@ function isParameter(str) {
|
|
1840
2079
|
const paramPattern = /^\$[0-9]+(::[a-zA-Z_][a-zA-Z0-9_]*)?$/;
|
1841
2080
|
return paramPattern.test(str);
|
1842
2081
|
}
|
1843
|
-
function isSingleRowResult(selectstmt,
|
1844
|
-
var _a
|
2082
|
+
function isSingleRowResult(selectstmt, fromColumns) {
|
2083
|
+
var _a;
|
1845
2084
|
const limit = checkLimit(selectstmt);
|
1846
2085
|
if (limit === 1) {
|
1847
2086
|
return true;
|
@@ -1855,11 +2094,11 @@ function isSingleRowResult(selectstmt, dbSchema) {
|
|
1855
2094
|
const simple_select_pramary = simple_select_pramary_list[0];
|
1856
2095
|
const from_clause = simple_select_pramary.from_clause();
|
1857
2096
|
if (!from_clause) {
|
1858
|
-
const hasSetReturningFunction = (
|
2097
|
+
const hasSetReturningFunction = hasFunction(simple_select_pramary, fun => isSetReturningFunction(fun));
|
1859
2098
|
return !hasSetReturningFunction;
|
1860
2099
|
}
|
1861
2100
|
if (!simple_select_pramary.group_clause()) {
|
1862
|
-
const agreegateFunction =
|
2101
|
+
const agreegateFunction = hasFunction(simple_select_pramary, fun => isAggregateFunction(fun));
|
1863
2102
|
if (agreegateFunction) {
|
1864
2103
|
return true;
|
1865
2104
|
}
|
@@ -1873,36 +2112,38 @@ function isSingleRowResult(selectstmt, dbSchema) {
|
|
1873
2112
|
}
|
1874
2113
|
const where_clause = simple_select_pramary.where_clause();
|
1875
2114
|
if (where_clause) {
|
1876
|
-
const
|
1877
|
-
const uniqueKeys = dbSchema.filter(col => col.table_name.toLowerCase() === tableName.name.toLowerCase()
|
1878
|
-
&& (col.column_key === 'PRI' || col.column_key === 'UNI'))
|
2115
|
+
const uniqueKeys = fromColumns.filter(col => col.column_key === 'PRI' || col.column_key === 'UNI')
|
1879
2116
|
.map(col => col.column_name);
|
1880
2117
|
return isSingleRowResult_where(where_clause.a_expr(), uniqueKeys);
|
1881
2118
|
}
|
1882
2119
|
return false;
|
1883
2120
|
}
|
1884
|
-
function
|
2121
|
+
function hasFunction(simple_select_pramary, checkFunction) {
|
1885
2122
|
const target_list_ = simple_select_pramary.target_list_();
|
1886
2123
|
if (target_list_) {
|
1887
|
-
return target_list_.target_list().target_el_list().some(target_el =>
|
2124
|
+
return target_list_.target_list().target_el_list().some(target_el => checkFunction_target_el(target_el, checkFunction));
|
1888
2125
|
}
|
1889
2126
|
const target_list = simple_select_pramary.target_list();
|
1890
|
-
|
2127
|
+
if (target_list) {
|
2128
|
+
return target_list.target_el_list().some(target_el => checkFunction_target_el(target_el, checkFunction));
|
2129
|
+
}
|
2130
|
+
return false;
|
1891
2131
|
}
|
1892
2132
|
function getTableName(table_ref) {
|
1893
2133
|
const relation_expr = table_ref.relation_expr();
|
1894
|
-
|
2134
|
+
if (relation_expr) {
|
2135
|
+
return traverse_relation_expr(relation_expr);
|
2136
|
+
}
|
1895
2137
|
const aliasClause = table_ref.alias_clause();
|
1896
|
-
const tableAlias = aliasClause ? aliasClause.colid().getText() : '';
|
1897
2138
|
return {
|
1898
|
-
name:
|
1899
|
-
alias:
|
2139
|
+
name: aliasClause.colid().getText(),
|
2140
|
+
alias: ''
|
1900
2141
|
};
|
1901
2142
|
}
|
1902
|
-
function
|
2143
|
+
function checkFunction_target_el(target_el, checkFunction) {
|
1903
2144
|
if (target_el instanceof PostgreSQLParser_1.Target_labelContext) {
|
1904
2145
|
const c_expr_list = collectContextsOfType(target_el, PostgreSQLParser_1.Func_exprContext, false);
|
1905
|
-
const aggrFunction = c_expr_list.some(func_expr =>
|
2146
|
+
const aggrFunction = c_expr_list.some(func_expr => checkFunction(func_expr));
|
1906
2147
|
return aggrFunction;
|
1907
2148
|
}
|
1908
2149
|
return false;
|
@@ -1955,25 +2196,98 @@ const aggregateFunctions = new Set([
|
|
1955
2196
|
'variance',
|
1956
2197
|
'xmlagg'
|
1957
2198
|
]);
|
1958
|
-
function
|
2199
|
+
function isAggregateFunction(func_expr) {
|
1959
2200
|
var _a, _b, _c;
|
1960
2201
|
const funcName = (_c = (_b = (_a = func_expr === null || func_expr === void 0 ? void 0 : func_expr.func_application()) === null || _a === void 0 ? void 0 : _a.func_name()) === null || _b === void 0 ? void 0 : _b.getText()) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
1961
2202
|
// RANK(), DENSE_RANK(), PERCENT_RANK() - they are window functions, not aggregate functions,
|
1962
2203
|
// even though your query is returning them from a join involving pg_aggregate.
|
1963
2204
|
return !func_expr.over_clause() && aggregateFunctions.has(funcName);
|
1964
2205
|
}
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
return false;
|
1972
|
-
}
|
1973
|
-
function isSetReturningFunction_c_expr(func_expr) {
|
2206
|
+
// SELECT distinct '''' || p.proname || ''',' AS function_name
|
2207
|
+
// FROM pg_proc p
|
2208
|
+
// JOIN pg_namespace n ON p.pronamespace = n.oid
|
2209
|
+
// WHERE p.proretset = true
|
2210
|
+
// ORDER BY function_name;
|
2211
|
+
function isSetReturningFunction(func_expr) {
|
1974
2212
|
var _a, _b, _c;
|
1975
2213
|
const funcName = (_c = (_b = (_a = func_expr === null || func_expr === void 0 ? void 0 : func_expr.func_application()) === null || _a === void 0 ? void 0 : _a.func_name()) === null || _b === void 0 ? void 0 : _b.getText()) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
1976
|
-
|
2214
|
+
const setReturning = new Set([
|
2215
|
+
'_pg_expandarray',
|
2216
|
+
'aclexplode',
|
2217
|
+
'generate_series',
|
2218
|
+
'generate_subscripts',
|
2219
|
+
'get_clients_with_addresses',
|
2220
|
+
'get_mytable1',
|
2221
|
+
'get_mytable1_by_id',
|
2222
|
+
'get_mytable1_with_nested_function',
|
2223
|
+
'get_mytable_plpgsql',
|
2224
|
+
'get_users_with_posts',
|
2225
|
+
'get_users_with_posts_plpgsql',
|
2226
|
+
'json_array_elements',
|
2227
|
+
'json_array_elements_text',
|
2228
|
+
'json_each',
|
2229
|
+
'json_each_text',
|
2230
|
+
'json_object_keys',
|
2231
|
+
'json_populate_recordset',
|
2232
|
+
'json_to_recordset',
|
2233
|
+
'jsonb_array_elements',
|
2234
|
+
'jsonb_array_elements_text',
|
2235
|
+
'jsonb_each',
|
2236
|
+
'jsonb_each_text',
|
2237
|
+
'jsonb_object_keys',
|
2238
|
+
'jsonb_path_query',
|
2239
|
+
'jsonb_populate_recordset',
|
2240
|
+
'jsonb_to_recordset',
|
2241
|
+
'pg_available_extension_versions',
|
2242
|
+
'pg_available_extensions',
|
2243
|
+
'pg_config',
|
2244
|
+
'pg_cursor',
|
2245
|
+
'pg_event_trigger_ddl_commands',
|
2246
|
+
'pg_event_trigger_dropped_objects',
|
2247
|
+
'pg_extension_update_paths',
|
2248
|
+
'pg_get_keywords',
|
2249
|
+
'pg_get_multixact_members',
|
2250
|
+
'pg_get_publication_tables',
|
2251
|
+
'pg_get_replication_slots',
|
2252
|
+
'pg_hba_file_rules',
|
2253
|
+
'pg_listening_channels',
|
2254
|
+
'pg_lock_status',
|
2255
|
+
'pg_logical_slot_get_binary_changes',
|
2256
|
+
'pg_logical_slot_get_changes',
|
2257
|
+
'pg_logical_slot_peek_binary_changes',
|
2258
|
+
'pg_logical_slot_peek_changes',
|
2259
|
+
'pg_ls_archive_statusdir',
|
2260
|
+
'pg_ls_dir',
|
2261
|
+
'pg_ls_logdir',
|
2262
|
+
'pg_ls_tmpdir',
|
2263
|
+
'pg_ls_waldir',
|
2264
|
+
'pg_mcv_list_items',
|
2265
|
+
'pg_options_to_table',
|
2266
|
+
'pg_partition_ancestors',
|
2267
|
+
'pg_partition_tree',
|
2268
|
+
'pg_prepared_statement',
|
2269
|
+
'pg_prepared_xact',
|
2270
|
+
'pg_show_all_file_settings',
|
2271
|
+
'pg_show_all_settings',
|
2272
|
+
'pg_show_replication_origin_status',
|
2273
|
+
'pg_stat_get_activity',
|
2274
|
+
'pg_stat_get_backend_idset',
|
2275
|
+
'pg_stat_get_progress_info',
|
2276
|
+
'pg_stat_get_wal_senders',
|
2277
|
+
'pg_stop_backup',
|
2278
|
+
'pg_tablespace_databases',
|
2279
|
+
'pg_timezone_abbrevs',
|
2280
|
+
'pg_timezone_names',
|
2281
|
+
'regexp_matches',
|
2282
|
+
'regexp_split_to_table',
|
2283
|
+
'ts_debug',
|
2284
|
+
'ts_parse',
|
2285
|
+
'ts_stat',
|
2286
|
+
'ts_token_type',
|
2287
|
+
'txid_snapshot_xip',
|
2288
|
+
'unnest'
|
2289
|
+
]);
|
2290
|
+
return setReturning.has(funcName);
|
1977
2291
|
}
|
1978
2292
|
function isSingleRowResult_where(a_expr, uniqueKeys) {
|
1979
2293
|
var _a, _b;
|