eslint-plugin-slonik 1.0.0
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/LICENSE +48 -0
- package/README.md +368 -0
- package/dist/config.cjs +61 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +192 -0
- package/dist/config.d.mts +192 -0
- package/dist/config.d.ts +192 -0
- package/dist/config.mjs +59 -0
- package/dist/config.mjs.map +1 -0
- package/dist/index.cjs +27 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +319 -0
- package/dist/index.d.mts +319 -0
- package/dist/index.d.ts +319 -0
- package/dist/index.mjs +20 -0
- package/dist/index.mjs.map +1 -0
- package/dist/shared/eslint-plugin-slonik.1m1xlVmw.d.cts +611 -0
- package/dist/shared/eslint-plugin-slonik.1m1xlVmw.d.mts +611 -0
- package/dist/shared/eslint-plugin-slonik.1m1xlVmw.d.ts +611 -0
- package/dist/shared/eslint-plugin-slonik.BxexVlk1.cjs +1539 -0
- package/dist/shared/eslint-plugin-slonik.BxexVlk1.cjs.map +1 -0
- package/dist/shared/eslint-plugin-slonik.C0xTyWZ2.mjs +2866 -0
- package/dist/shared/eslint-plugin-slonik.C0xTyWZ2.mjs.map +1 -0
- package/dist/shared/eslint-plugin-slonik.DbzoLz5_.mjs +1514 -0
- package/dist/shared/eslint-plugin-slonik.DbzoLz5_.mjs.map +1 -0
- package/dist/shared/eslint-plugin-slonik.rlOTrCdf.cjs +2929 -0
- package/dist/shared/eslint-plugin-slonik.rlOTrCdf.cjs.map +1 -0
- package/dist/workers/check-sql.worker.cjs +2436 -0
- package/dist/workers/check-sql.worker.cjs.map +1 -0
- package/dist/workers/check-sql.worker.d.cts +171 -0
- package/dist/workers/check-sql.worker.d.mts +171 -0
- package/dist/workers/check-sql.worker.d.ts +171 -0
- package/dist/workers/check-sql.worker.mjs +2412 -0
- package/dist/workers/check-sql.worker.mjs.map +1 -0
- package/package.json +103 -0
|
@@ -0,0 +1,2436 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const checkSql_utils = require('../shared/eslint-plugin-slonik.rlOTrCdf.cjs');
|
|
4
|
+
const fpTs = require('fp-ts');
|
|
5
|
+
const parser = require('libpg-query');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const synckit = require('synckit');
|
|
8
|
+
const tsPattern = require('ts-pattern');
|
|
9
|
+
const postgres = require('postgres');
|
|
10
|
+
require('fp-ts/lib/Either.js');
|
|
11
|
+
const function_js = require('fp-ts/lib/function.js');
|
|
12
|
+
const O = require('fp-ts/lib/Option.js');
|
|
13
|
+
const TE = require('fp-ts/lib/TaskEither.js');
|
|
14
|
+
const J = require('fp-ts/lib/Json.js');
|
|
15
|
+
const chokidar = require('chokidar');
|
|
16
|
+
require('crypto');
|
|
17
|
+
require('fs');
|
|
18
|
+
require('@typescript-eslint/utils');
|
|
19
|
+
require('pg-connection-string');
|
|
20
|
+
|
|
21
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
22
|
+
|
|
23
|
+
function _interopNamespaceCompat(e) {
|
|
24
|
+
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
25
|
+
const n = Object.create(null);
|
|
26
|
+
if (e) {
|
|
27
|
+
for (const k in e) {
|
|
28
|
+
n[k] = e[k];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
n.default = e;
|
|
32
|
+
return n;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const parser__namespace = /*#__PURE__*/_interopNamespaceCompat(parser);
|
|
36
|
+
const path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
37
|
+
const postgres__default = /*#__PURE__*/_interopDefaultCompat(postgres);
|
|
38
|
+
const O__namespace = /*#__PURE__*/_interopNamespaceCompat(O);
|
|
39
|
+
const TE__namespace = /*#__PURE__*/_interopNamespaceCompat(TE);
|
|
40
|
+
const J__namespace = /*#__PURE__*/_interopNamespaceCompat(J);
|
|
41
|
+
const chokidar__default = /*#__PURE__*/_interopDefaultCompat(chokidar);
|
|
42
|
+
|
|
43
|
+
var AExprKind = /* @__PURE__ */ ((AExprKind2) => {
|
|
44
|
+
AExprKind2["A_EXPR_KIND_UNDEFINED"] = "A_EXPR_KIND_UNDEFINED";
|
|
45
|
+
AExprKind2["AEXPR_OP"] = "AEXPR_OP";
|
|
46
|
+
AExprKind2["AEXPR_OP_ANY"] = "AEXPR_OP_ANY";
|
|
47
|
+
AExprKind2["AEXPR_OP_ALL"] = "AEXPR_OP_ALL";
|
|
48
|
+
AExprKind2["AEXPR_DISTINCT"] = "AEXPR_DISTINCT";
|
|
49
|
+
AExprKind2["AEXPR_NOT_DISTINCT"] = "AEXPR_NOT_DISTINCT";
|
|
50
|
+
AExprKind2["AEXPR_NULLIF"] = "AEXPR_NULLIF";
|
|
51
|
+
AExprKind2["AEXPR_IN"] = "AEXPR_IN";
|
|
52
|
+
AExprKind2["AEXPR_LIKE"] = "AEXPR_LIKE";
|
|
53
|
+
AExprKind2["AEXPR_ILIKE"] = "AEXPR_ILIKE";
|
|
54
|
+
AExprKind2["AEXPR_SIMILAR"] = "AEXPR_SIMILAR";
|
|
55
|
+
AExprKind2["AEXPR_BETWEEN"] = "AEXPR_BETWEEN";
|
|
56
|
+
AExprKind2["AEXPR_NOT_BETWEEN"] = "AEXPR_NOT_BETWEEN";
|
|
57
|
+
AExprKind2["AEXPR_BETWEEN_SYM"] = "AEXPR_BETWEEN_SYM";
|
|
58
|
+
AExprKind2["AEXPR_NOT_BETWEEN_SYM"] = "AEXPR_NOT_BETWEEN_SYM";
|
|
59
|
+
AExprKind2["UNRECOGNIZED"] = "UNRECOGNIZED";
|
|
60
|
+
return AExprKind2;
|
|
61
|
+
})(AExprKind || {});
|
|
62
|
+
var BoolExprType = /* @__PURE__ */ ((BoolExprType2) => {
|
|
63
|
+
BoolExprType2["BOOL_EXPR_TYPE_UNDEFINED"] = "BOOL_EXPR_TYPE_UNDEFINED";
|
|
64
|
+
BoolExprType2["AND_EXPR"] = "AND_EXPR";
|
|
65
|
+
BoolExprType2["OR_EXPR"] = "OR_EXPR";
|
|
66
|
+
BoolExprType2["NOT_EXPR"] = "NOT_EXPR";
|
|
67
|
+
BoolExprType2["UNRECOGNIZED"] = "UNRECOGNIZED";
|
|
68
|
+
return BoolExprType2;
|
|
69
|
+
})(BoolExprType || {});
|
|
70
|
+
var SubLinkType = /* @__PURE__ */ ((SubLinkType2) => {
|
|
71
|
+
SubLinkType2["SUB_LINK_TYPE_UNDEFINED"] = "SUB_LINK_TYPE_UNDEFINED";
|
|
72
|
+
SubLinkType2["EXISTS_SUBLINK"] = "EXISTS_SUBLINK";
|
|
73
|
+
SubLinkType2["ALL_SUBLINK"] = "ALL_SUBLINK";
|
|
74
|
+
SubLinkType2["ANY_SUBLINK"] = "ANY_SUBLINK";
|
|
75
|
+
SubLinkType2["ROWCOMPARE_SUBLINK"] = "ROWCOMPARE_SUBLINK";
|
|
76
|
+
SubLinkType2["EXPR_SUBLINK"] = "EXPR_SUBLINK";
|
|
77
|
+
SubLinkType2["MULTIEXPR_SUBLINK"] = "MULTIEXPR_SUBLINK";
|
|
78
|
+
SubLinkType2["ARRAY_SUBLINK"] = "ARRAY_SUBLINK";
|
|
79
|
+
SubLinkType2["CTE_SUBLINK"] = "CTE_SUBLINK";
|
|
80
|
+
SubLinkType2["UNRECOGNIZED"] = "UNRECOGNIZED";
|
|
81
|
+
return SubLinkType2;
|
|
82
|
+
})(SubLinkType || {});
|
|
83
|
+
var NullTestType = /* @__PURE__ */ ((NullTestType2) => {
|
|
84
|
+
NullTestType2["NULL_TEST_TYPE_UNDEFINED"] = "NULL_TEST_TYPE_UNDEFINED";
|
|
85
|
+
NullTestType2["IS_NULL"] = "IS_NULL";
|
|
86
|
+
NullTestType2["IS_NOT_NULL"] = "IS_NOT_NULL";
|
|
87
|
+
NullTestType2["UNRECOGNIZED"] = "UNRECOGNIZED";
|
|
88
|
+
return NullTestType2;
|
|
89
|
+
})(NullTestType || {});
|
|
90
|
+
var JoinType = /* @__PURE__ */ ((JoinType2) => {
|
|
91
|
+
JoinType2["JOIN_TYPE_UNDEFINED"] = "JOIN_TYPE_UNDEFINED";
|
|
92
|
+
JoinType2["JOIN_INNER"] = "JOIN_INNER";
|
|
93
|
+
JoinType2["JOIN_LEFT"] = "JOIN_LEFT";
|
|
94
|
+
JoinType2["JOIN_FULL"] = "JOIN_FULL";
|
|
95
|
+
JoinType2["JOIN_RIGHT"] = "JOIN_RIGHT";
|
|
96
|
+
JoinType2["JOIN_SEMI"] = "JOIN_SEMI";
|
|
97
|
+
JoinType2["JOIN_ANTI"] = "JOIN_ANTI";
|
|
98
|
+
JoinType2["JOIN_UNIQUE_OUTER"] = "JOIN_UNIQUE_OUTER";
|
|
99
|
+
JoinType2["JOIN_UNIQUE_INNER"] = "JOIN_UNIQUE_INNER";
|
|
100
|
+
JoinType2["UNRECOGNIZED"] = "UNRECOGNIZED";
|
|
101
|
+
return JoinType2;
|
|
102
|
+
})(JoinType || {});
|
|
103
|
+
|
|
104
|
+
function isColumnStarRef(fields) {
|
|
105
|
+
return isSingleCell(fields) && fields[0]?.A_Star !== void 0;
|
|
106
|
+
}
|
|
107
|
+
function isColumnTableStarRef(fields) {
|
|
108
|
+
return isTuple(fields) && fields[0].String !== void 0 && fields[1].A_Star !== void 0;
|
|
109
|
+
}
|
|
110
|
+
function isColumnUnknownRef(fields) {
|
|
111
|
+
return isSingleCell(fields) && fields[0].String !== void 0;
|
|
112
|
+
}
|
|
113
|
+
function isColumnTableColumnRef(fields) {
|
|
114
|
+
return isTuple(fields) && fields[0].String !== void 0 && fields[1].String !== void 0;
|
|
115
|
+
}
|
|
116
|
+
function isSingleCell(arr) {
|
|
117
|
+
return arr.length === 1;
|
|
118
|
+
}
|
|
119
|
+
function isTuple(arr) {
|
|
120
|
+
return arr.length === 2;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getSources({
|
|
124
|
+
select,
|
|
125
|
+
prevSources,
|
|
126
|
+
nonNullableColumns,
|
|
127
|
+
pgColsBySchemaAndTableName,
|
|
128
|
+
relations
|
|
129
|
+
}) {
|
|
130
|
+
const relationsByAlias = /* @__PURE__ */ new Map();
|
|
131
|
+
const relationsByRelName = /* @__PURE__ */ new Map();
|
|
132
|
+
for (const rel of relations) {
|
|
133
|
+
const key = rel.alias ?? rel.joinRelName;
|
|
134
|
+
if (key) {
|
|
135
|
+
relationsByAlias.set(key, rel);
|
|
136
|
+
}
|
|
137
|
+
if (!relationsByRelName.has(rel.relName)) {
|
|
138
|
+
relationsByRelName.set(rel.relName, []);
|
|
139
|
+
}
|
|
140
|
+
relationsByRelName.get(rel.relName).push(rel);
|
|
141
|
+
}
|
|
142
|
+
const tableToSchema = /* @__PURE__ */ new Map();
|
|
143
|
+
const publicCols = pgColsBySchemaAndTableName.get("public");
|
|
144
|
+
if (publicCols) {
|
|
145
|
+
for (const tableName of publicCols.keys()) {
|
|
146
|
+
tableToSchema.set(tableName, "public");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const [schemaName, cols] of pgColsBySchemaAndTableName) {
|
|
150
|
+
if (schemaName === "public") break;
|
|
151
|
+
for (const tableName of cols.keys()) {
|
|
152
|
+
if (!tableToSchema.has(tableName)) {
|
|
153
|
+
tableToSchema.set(tableName, schemaName);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function getColumnCTEs(ctes2) {
|
|
158
|
+
const map = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const cte of ctes2) {
|
|
160
|
+
if (cte.CommonTableExpr?.ctequery?.SelectStmt === void 0) continue;
|
|
161
|
+
if (cte.CommonTableExpr?.ctename === void 0) continue;
|
|
162
|
+
const resolver = getSources({
|
|
163
|
+
pgColsBySchemaAndTableName,
|
|
164
|
+
prevSources,
|
|
165
|
+
nonNullableColumns,
|
|
166
|
+
relations,
|
|
167
|
+
select: cte.CommonTableExpr.ctequery.SelectStmt
|
|
168
|
+
});
|
|
169
|
+
map.set(cte.CommonTableExpr.ctename, resolver);
|
|
170
|
+
}
|
|
171
|
+
return map;
|
|
172
|
+
}
|
|
173
|
+
const ctes = getColumnCTEs(select.withClause?.ctes ?? []);
|
|
174
|
+
function resolveRangeVarSchema(node) {
|
|
175
|
+
switch (true) {
|
|
176
|
+
case node.schemaname !== void 0:
|
|
177
|
+
return node.schemaname;
|
|
178
|
+
case pgColsBySchemaAndTableName.get("public")?.has(node.relname):
|
|
179
|
+
return "public";
|
|
180
|
+
default:
|
|
181
|
+
for (const [schemaName, cols] of pgColsBySchemaAndTableName) {
|
|
182
|
+
if (cols.has(node.relname)) {
|
|
183
|
+
return schemaName;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return "public";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function getNodeColumnAndSources(node) {
|
|
190
|
+
if (node.RangeVar !== void 0) {
|
|
191
|
+
const cteResolver = ctes.get(node.RangeVar.relname);
|
|
192
|
+
if (cteResolver !== void 0) {
|
|
193
|
+
return [{ kind: "cte", name: node.RangeVar.relname, sources: cteResolver }];
|
|
194
|
+
}
|
|
195
|
+
const schemaName = resolveRangeVarSchema(node.RangeVar);
|
|
196
|
+
const realTableName = node.RangeVar.relname;
|
|
197
|
+
const tableName = node.RangeVar.alias?.aliasname ?? realTableName;
|
|
198
|
+
const tableColsArray = pgColsBySchemaAndTableName.get(schemaName)?.get(realTableName) ?? [];
|
|
199
|
+
const tableSource = {
|
|
200
|
+
kind: "table",
|
|
201
|
+
schemaName,
|
|
202
|
+
original: realTableName,
|
|
203
|
+
name: tableName,
|
|
204
|
+
alias: node.RangeVar.alias?.aliasname,
|
|
205
|
+
columns: /* @__PURE__ */ new Map()
|
|
206
|
+
};
|
|
207
|
+
for (const col of tableColsArray) {
|
|
208
|
+
tableSource.columns.set(col.colName, resolveColumn(col, tableSource));
|
|
209
|
+
}
|
|
210
|
+
return [tableSource];
|
|
211
|
+
}
|
|
212
|
+
const sourcesArr = [];
|
|
213
|
+
if (node.JoinExpr?.larg !== void 0) {
|
|
214
|
+
sourcesArr.push(...getNodeColumnAndSources(node.JoinExpr.larg));
|
|
215
|
+
}
|
|
216
|
+
if (node.JoinExpr?.rarg !== void 0) {
|
|
217
|
+
sourcesArr.push(...getNodeColumnAndSources(node.JoinExpr.rarg));
|
|
218
|
+
}
|
|
219
|
+
if (node.RangeSubselect?.subquery?.SelectStmt?.fromClause !== void 0) {
|
|
220
|
+
const combinedPrevSources = new Map([
|
|
221
|
+
...prevSources?.entries() ?? [],
|
|
222
|
+
...sourcesArr.map((x) => [x.name, x])
|
|
223
|
+
]);
|
|
224
|
+
sourcesArr.push({
|
|
225
|
+
kind: "subselect",
|
|
226
|
+
name: node.RangeSubselect.alias?.aliasname ?? "subselect",
|
|
227
|
+
sources: getSources({
|
|
228
|
+
nonNullableColumns,
|
|
229
|
+
pgColsBySchemaAndTableName,
|
|
230
|
+
relations,
|
|
231
|
+
prevSources: combinedPrevSources,
|
|
232
|
+
select: node.RangeSubselect.subquery.SelectStmt
|
|
233
|
+
})
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return sourcesArr;
|
|
237
|
+
}
|
|
238
|
+
function getColumnSources(stmt) {
|
|
239
|
+
const fromClauseSources = [];
|
|
240
|
+
for (const node of stmt.fromClause ?? []) {
|
|
241
|
+
const nodes = getNodeColumnAndSources(node);
|
|
242
|
+
for (const nodeSource of nodes) {
|
|
243
|
+
fromClauseSources.push([nodeSource.name, nodeSource]);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return new Map(fromClauseSources);
|
|
247
|
+
}
|
|
248
|
+
const sources = new Map([
|
|
249
|
+
...prevSources?.entries() ?? [],
|
|
250
|
+
...getColumnSources(select).entries()
|
|
251
|
+
]);
|
|
252
|
+
const cachedColumnsMap = /* @__PURE__ */ new WeakMap();
|
|
253
|
+
function getSourceColumns(source) {
|
|
254
|
+
if (cachedColumnsMap.has(source)) {
|
|
255
|
+
return cachedColumnsMap.get(source);
|
|
256
|
+
}
|
|
257
|
+
let result = [];
|
|
258
|
+
switch (source.kind) {
|
|
259
|
+
case "table":
|
|
260
|
+
result = Array.from(source.columns.values()).map((col) => ({
|
|
261
|
+
column: col,
|
|
262
|
+
source
|
|
263
|
+
}));
|
|
264
|
+
break;
|
|
265
|
+
case "cte":
|
|
266
|
+
case "subselect":
|
|
267
|
+
result = [];
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
cachedColumnsMap.set(source, result);
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
function getAllResolvedColumns() {
|
|
274
|
+
const all = [];
|
|
275
|
+
for (const source of sources.values()) {
|
|
276
|
+
all.push(...getSourceColumns(source));
|
|
277
|
+
}
|
|
278
|
+
return all;
|
|
279
|
+
}
|
|
280
|
+
const allResolved = getAllResolvedColumns();
|
|
281
|
+
const columnIndex = /* @__PURE__ */ new Map();
|
|
282
|
+
const unknownColumnIndex = /* @__PURE__ */ new Map();
|
|
283
|
+
for (const entry of allResolved) {
|
|
284
|
+
const key = `${entry.source.name}.${entry.column.column.colName}`;
|
|
285
|
+
columnIndex.set(key, entry);
|
|
286
|
+
const unknownKey = entry.column.column.colName;
|
|
287
|
+
if (!unknownColumnIndex.has(unknownKey)) {
|
|
288
|
+
unknownColumnIndex.set(unknownKey, []);
|
|
289
|
+
}
|
|
290
|
+
unknownColumnIndex.get(unknownKey).push(entry);
|
|
291
|
+
}
|
|
292
|
+
function getColumnByTableAndColumnName(p) {
|
|
293
|
+
const source = sources.get(p.table);
|
|
294
|
+
if (!source) return null;
|
|
295
|
+
switch (source.kind) {
|
|
296
|
+
case "table":
|
|
297
|
+
return source.columns.get(p.column) || null;
|
|
298
|
+
default:
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function getResolvedColumnsInTable(sourceName) {
|
|
303
|
+
const source = sources.get(sourceName);
|
|
304
|
+
if (!source) return [];
|
|
305
|
+
return getSourceColumns(source).map((x) => x.column);
|
|
306
|
+
}
|
|
307
|
+
function getNestedResolvedTargetField(field) {
|
|
308
|
+
switch (field.kind) {
|
|
309
|
+
case "column": {
|
|
310
|
+
const key = `${field.table}.${field.column}`;
|
|
311
|
+
if (columnIndex.has(key)) {
|
|
312
|
+
return columnIndex.get(key)?.column ?? null;
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case "unknown": {
|
|
317
|
+
if (unknownColumnIndex.has(field.field)) {
|
|
318
|
+
const candidates = unknownColumnIndex.get(field.field);
|
|
319
|
+
if (candidates.length > 0) return candidates[0]?.column ?? null;
|
|
320
|
+
}
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const source of sources.values()) {
|
|
325
|
+
switch (source.kind) {
|
|
326
|
+
case "cte":
|
|
327
|
+
case "subselect": {
|
|
328
|
+
const nested = source.sources.getNestedResolvedTargetField(field);
|
|
329
|
+
if (nested) {
|
|
330
|
+
return {
|
|
331
|
+
...nested,
|
|
332
|
+
isNotNull: nested.isNotNull && !checkIsNullableDueToRelation(source.name)
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
case "table": {
|
|
338
|
+
const key = field.kind === "column" ? field.column : field.field;
|
|
339
|
+
const column = source.columns.get(key);
|
|
340
|
+
if (column) return column;
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
function getColumnsByTargetField(field) {
|
|
348
|
+
switch (field.kind) {
|
|
349
|
+
case "column": {
|
|
350
|
+
const result = getColumnByTableAndColumnName(field);
|
|
351
|
+
if (result !== null) {
|
|
352
|
+
return [result];
|
|
353
|
+
}
|
|
354
|
+
for (const source of sources.values()) {
|
|
355
|
+
switch (source.kind) {
|
|
356
|
+
case "subselect": {
|
|
357
|
+
const column = source.sources.getNestedResolvedTargetField(field);
|
|
358
|
+
if (column) {
|
|
359
|
+
const isNullableDueToRelation = checkIsNullableDueToRelation(source.name);
|
|
360
|
+
if (isNullableDueToRelation && column.isNotNull) {
|
|
361
|
+
return [{ ...column, isNotNull: false }];
|
|
362
|
+
}
|
|
363
|
+
return [column];
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
case "unknown": {
|
|
372
|
+
const source = sources.get(field.field);
|
|
373
|
+
if (source !== void 0) {
|
|
374
|
+
return getSourceColumns(source).map((x) => x.column);
|
|
375
|
+
}
|
|
376
|
+
const foundColumn = getNestedResolvedTargetField(field);
|
|
377
|
+
if (foundColumn) {
|
|
378
|
+
return [foundColumn];
|
|
379
|
+
}
|
|
380
|
+
for (const source2 of sources.values()) {
|
|
381
|
+
switch (source2.kind) {
|
|
382
|
+
case "subselect": {
|
|
383
|
+
const columns = source2.sources.getColumnsByTargetField(field);
|
|
384
|
+
if (columns) {
|
|
385
|
+
const isNullableDueToRelation = checkIsNullableDueToRelation(source2.name);
|
|
386
|
+
if (isNullableDueToRelation) {
|
|
387
|
+
return columns.map((col) => col.isNotNull ? { ...col, isNotNull: false } : col);
|
|
388
|
+
}
|
|
389
|
+
return columns;
|
|
390
|
+
}
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function checkIsNullableDueToRelation(key) {
|
|
400
|
+
const rel = relationsByAlias.get(key);
|
|
401
|
+
if (rel !== void 0) {
|
|
402
|
+
switch (rel.joinType) {
|
|
403
|
+
case JoinType.JOIN_LEFT:
|
|
404
|
+
case JoinType.JOIN_FULL:
|
|
405
|
+
return true;
|
|
406
|
+
case JoinType.JOIN_TYPE_UNDEFINED:
|
|
407
|
+
case JoinType.JOIN_INNER:
|
|
408
|
+
case JoinType.JOIN_RIGHT:
|
|
409
|
+
case JoinType.JOIN_SEMI:
|
|
410
|
+
case JoinType.JOIN_ANTI:
|
|
411
|
+
case JoinType.JOIN_UNIQUE_OUTER:
|
|
412
|
+
case JoinType.JOIN_UNIQUE_INNER:
|
|
413
|
+
case JoinType.UNRECOGNIZED:
|
|
414
|
+
return false;
|
|
415
|
+
default:
|
|
416
|
+
return checkSql_utils.assertNever(rel.joinType);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const relation of relationsByRelName.get(key) ?? []) {
|
|
420
|
+
switch (relation.joinType) {
|
|
421
|
+
case JoinType.JOIN_RIGHT:
|
|
422
|
+
case JoinType.JOIN_FULL:
|
|
423
|
+
return true;
|
|
424
|
+
case JoinType.JOIN_TYPE_UNDEFINED:
|
|
425
|
+
case JoinType.JOIN_INNER:
|
|
426
|
+
case JoinType.JOIN_LEFT:
|
|
427
|
+
case JoinType.JOIN_SEMI:
|
|
428
|
+
case JoinType.JOIN_ANTI:
|
|
429
|
+
case JoinType.JOIN_UNIQUE_OUTER:
|
|
430
|
+
case JoinType.JOIN_UNIQUE_INNER:
|
|
431
|
+
case JoinType.UNRECOGNIZED:
|
|
432
|
+
return false;
|
|
433
|
+
default:
|
|
434
|
+
return checkSql_utils.assertNever(relation.joinType);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
function resolveColumn(col, source) {
|
|
440
|
+
const keyForNullability = source.alias ? source.alias : source.original;
|
|
441
|
+
const isNullableDueToRelation = checkIsNullableDueToRelation(keyForNullability);
|
|
442
|
+
const isNotNullBasedOnAST = nonNullableColumns.has(col.colName) || nonNullableColumns.has(`${source.name}.${col.colName}`);
|
|
443
|
+
const isNotNullInTable = col.colNotNull;
|
|
444
|
+
const isNonNullable = isNotNullBasedOnAST || isNotNullInTable && !isNullableDueToRelation;
|
|
445
|
+
return { column: col, isNotNull: isNonNullable };
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
getNodeColumnAndSources,
|
|
449
|
+
getResolvedColumnsInTable,
|
|
450
|
+
getAllResolvedColumns,
|
|
451
|
+
getColumnsByTargetField,
|
|
452
|
+
getNestedResolvedTargetField,
|
|
453
|
+
sources
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const nonNullFunctions = /* @__PURE__ */ new Set([
|
|
458
|
+
"count",
|
|
459
|
+
"now",
|
|
460
|
+
"abs",
|
|
461
|
+
"ceil",
|
|
462
|
+
"floor",
|
|
463
|
+
"round",
|
|
464
|
+
"sqrt",
|
|
465
|
+
"cbrt",
|
|
466
|
+
"exp",
|
|
467
|
+
"log",
|
|
468
|
+
"log10",
|
|
469
|
+
"sin",
|
|
470
|
+
"cos",
|
|
471
|
+
"tan",
|
|
472
|
+
"asin",
|
|
473
|
+
"acos",
|
|
474
|
+
"atan",
|
|
475
|
+
"radians",
|
|
476
|
+
"degrees",
|
|
477
|
+
"length",
|
|
478
|
+
"trim",
|
|
479
|
+
"lower",
|
|
480
|
+
"upper",
|
|
481
|
+
"ascii",
|
|
482
|
+
"concat",
|
|
483
|
+
"concat_ws",
|
|
484
|
+
"lpad",
|
|
485
|
+
"rpad",
|
|
486
|
+
"initcap",
|
|
487
|
+
"left",
|
|
488
|
+
"right",
|
|
489
|
+
"cdat",
|
|
490
|
+
"chr",
|
|
491
|
+
"strpos",
|
|
492
|
+
"substr",
|
|
493
|
+
"translate",
|
|
494
|
+
"current_date",
|
|
495
|
+
"current_time",
|
|
496
|
+
"current_timestamp",
|
|
497
|
+
"localtime",
|
|
498
|
+
"localtimestamp",
|
|
499
|
+
"uuid_generate_v1",
|
|
500
|
+
"uuid_generate_v4",
|
|
501
|
+
"pi",
|
|
502
|
+
"random",
|
|
503
|
+
"exists",
|
|
504
|
+
"row_number",
|
|
505
|
+
"current_schema",
|
|
506
|
+
"current_schemas",
|
|
507
|
+
"inet_server_addr",
|
|
508
|
+
"inet_server_port",
|
|
509
|
+
"isfinite",
|
|
510
|
+
"isnan",
|
|
511
|
+
"pg_backend_pid",
|
|
512
|
+
"pg_blocking_pids",
|
|
513
|
+
"pg_cancel_backend",
|
|
514
|
+
"pg_is_in_recovery",
|
|
515
|
+
"pg_postmaster_start_time",
|
|
516
|
+
"pg_relation_size",
|
|
517
|
+
"pg_total_relation_size",
|
|
518
|
+
"timeofday",
|
|
519
|
+
"similar_to_escape"
|
|
520
|
+
]);
|
|
521
|
+
function concatStringNodes$1(nodes) {
|
|
522
|
+
return nodes?.map((x) => x.String?.sval).filter(Boolean).join(".") ?? "";
|
|
523
|
+
}
|
|
524
|
+
function isColumnNonNullable(val, root) {
|
|
525
|
+
if (val === void 0) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
if (val.NullTest) {
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
if (val.BoolExpr?.boolop === BoolExprType.NOT_EXPR) {
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
if (val.A_Const) {
|
|
535
|
+
return val.A_Const.isnull !== true;
|
|
536
|
+
}
|
|
537
|
+
if (val.FuncCall) {
|
|
538
|
+
const functionName = concatStringNodes$1(val.FuncCall.funcname);
|
|
539
|
+
return nonNullFunctions.has(functionName.toLowerCase());
|
|
540
|
+
}
|
|
541
|
+
if (val.TypeCast?.arg) {
|
|
542
|
+
return isColumnNonNullable(val.TypeCast.arg, root);
|
|
543
|
+
}
|
|
544
|
+
if (val.SubLink) {
|
|
545
|
+
switch (val.SubLink.subLinkType) {
|
|
546
|
+
case SubLinkType.EXISTS_SUBLINK:
|
|
547
|
+
return true;
|
|
548
|
+
case SubLinkType.ARRAY_SUBLINK:
|
|
549
|
+
return true;
|
|
550
|
+
// while the array itself can be non-nullable, the elements can be nullable
|
|
551
|
+
case SubLinkType.ALL_SUBLINK:
|
|
552
|
+
case SubLinkType.ANY_SUBLINK:
|
|
553
|
+
case SubLinkType.ROWCOMPARE_SUBLINK:
|
|
554
|
+
case SubLinkType.EXPR_SUBLINK:
|
|
555
|
+
case SubLinkType.MULTIEXPR_SUBLINK:
|
|
556
|
+
case SubLinkType.CTE_SUBLINK:
|
|
557
|
+
return isColumnNonNullable(val.SubLink.subselect, root);
|
|
558
|
+
case SubLinkType.SUB_LINK_TYPE_UNDEFINED:
|
|
559
|
+
case SubLinkType.UNRECOGNIZED:
|
|
560
|
+
return false;
|
|
561
|
+
default:
|
|
562
|
+
checkSql_utils.assertNever(val.SubLink.subLinkType);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (val.A_Expr?.kind === AExprKind.AEXPR_SIMILAR) {
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
if (val.A_Expr?.kind === AExprKind.AEXPR_LIKE) {
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
if (val.A_Expr?.kind === AExprKind.AEXPR_OP) {
|
|
572
|
+
return isColumnNonNullable(val.A_Expr.lexpr, root) && isColumnNonNullable(val.A_Expr.rexpr, root);
|
|
573
|
+
}
|
|
574
|
+
if (val.CaseExpr) {
|
|
575
|
+
for (const when of val.CaseExpr.args) {
|
|
576
|
+
if (!isColumnNonNullable(when.CaseWhen?.result, root)) {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (val.CaseExpr.defresult && !isColumnNonNullable(val.CaseExpr.defresult, root)) {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
if (val.ColumnRef) {
|
|
586
|
+
const refColumnName = concatStringNodes$1(val.ColumnRef.fields);
|
|
587
|
+
for (const stmt of root.stmts) {
|
|
588
|
+
if (stmt?.stmt?.SelectStmt?.whereClause) {
|
|
589
|
+
const whereClause = stmt.stmt.SelectStmt.whereClause;
|
|
590
|
+
const whereClauseColumnName = concatStringNodes$1(
|
|
591
|
+
whereClause.NullTest?.arg?.ColumnRef?.fields
|
|
592
|
+
);
|
|
593
|
+
if (whereClause.NullTest?.nulltesttype === NullTestType.IS_NOT_NULL && whereClauseColumnName === refColumnName) {
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (val.CoalesceExpr) {
|
|
600
|
+
for (const arg of val.CoalesceExpr.args) {
|
|
601
|
+
if (isColumnNonNullable(arg, root)) {
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (val.A_ArrayExpr) {
|
|
607
|
+
return true;
|
|
608
|
+
}
|
|
609
|
+
if (val.SelectStmt) {
|
|
610
|
+
const nonNullableColumnsInSubStmt = getNonNullableColumnsInSelectStmt(val.SelectStmt, root);
|
|
611
|
+
return Array.from(nonNullableColumnsInSubStmt.values()).some(Boolean);
|
|
612
|
+
}
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
function getNodeName(node) {
|
|
616
|
+
if (node?.ColumnRef !== void 0) {
|
|
617
|
+
return concatStringNodes$1(node.ColumnRef.fields);
|
|
618
|
+
}
|
|
619
|
+
if (node?.NullTest) {
|
|
620
|
+
return getNodeName(node.NullTest.arg);
|
|
621
|
+
}
|
|
622
|
+
if (node?.A_Const?.boolval !== void 0) {
|
|
623
|
+
return "bool";
|
|
624
|
+
}
|
|
625
|
+
if (node?.TypeCast !== void 0) {
|
|
626
|
+
const typeName = concatStringNodes$1(node.TypeCast.typeName?.names);
|
|
627
|
+
if (typeName === "pg_catalog.interval") {
|
|
628
|
+
return "interval";
|
|
629
|
+
}
|
|
630
|
+
if (node.TypeCast.arg) {
|
|
631
|
+
return getNodeName(node.TypeCast.arg);
|
|
632
|
+
}
|
|
633
|
+
return typeName.replace(/^pg_catalog\./, "");
|
|
634
|
+
}
|
|
635
|
+
if (node?.FuncCall?.funcname !== void 0) {
|
|
636
|
+
return concatStringNodes$1(node.FuncCall.funcname);
|
|
637
|
+
}
|
|
638
|
+
if (node?.SubLink?.subLinkType === SubLinkType.EXISTS_SUBLINK) {
|
|
639
|
+
return "exists";
|
|
640
|
+
}
|
|
641
|
+
if (node?.SubLink?.subLinkType === SubLinkType.ARRAY_SUBLINK) {
|
|
642
|
+
return "array";
|
|
643
|
+
}
|
|
644
|
+
if (node?.SubLink?.subselect?.SelectStmt?.targetList !== void 0) {
|
|
645
|
+
const subSelectTargetList = node.SubLink.subselect.SelectStmt.targetList;
|
|
646
|
+
if (subSelectTargetList?.[0].ResTarget !== void 0) {
|
|
647
|
+
return getTargetName(subSelectTargetList[0].ResTarget);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (node?.A_ArrayExpr !== void 0) {
|
|
651
|
+
return "array";
|
|
652
|
+
}
|
|
653
|
+
if (node?.CaseExpr !== void 0) {
|
|
654
|
+
return "case";
|
|
655
|
+
}
|
|
656
|
+
if (node?.CoalesceExpr !== void 0) {
|
|
657
|
+
return "coalesce";
|
|
658
|
+
}
|
|
659
|
+
return "?column?";
|
|
660
|
+
}
|
|
661
|
+
function getTargetName(target) {
|
|
662
|
+
return target.name ?? getNodeName(target.val);
|
|
663
|
+
}
|
|
664
|
+
function getNonNullableColumnsInSelectStmt(stmt, root) {
|
|
665
|
+
const nonNullableColumns = /* @__PURE__ */ new Set();
|
|
666
|
+
const targetList = [
|
|
667
|
+
...stmt.targetList ?? [],
|
|
668
|
+
...stmt.larg?.targetList ?? [],
|
|
669
|
+
...stmt.rarg?.targetList ?? []
|
|
670
|
+
];
|
|
671
|
+
const targetNames = /* @__PURE__ */ new Set();
|
|
672
|
+
for (const target of targetList) {
|
|
673
|
+
if (target.ResTarget) {
|
|
674
|
+
targetNames.add(getTargetName(target.ResTarget));
|
|
675
|
+
}
|
|
676
|
+
if (target.ResTarget && isColumnNonNullable(target.ResTarget.val, root)) {
|
|
677
|
+
nonNullableColumns.add(getTargetName(target.ResTarget));
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (stmt.whereClause) {
|
|
681
|
+
if (stmt.whereClause.NullTest?.nulltesttype === NullTestType.IS_NOT_NULL) {
|
|
682
|
+
const colRef = stmt.whereClause.NullTest.arg?.ColumnRef;
|
|
683
|
+
const whereClauseName = concatStringNodes$1(colRef?.fields);
|
|
684
|
+
switch (true) {
|
|
685
|
+
case targetNames.has(whereClauseName):
|
|
686
|
+
nonNullableColumns.add(whereClauseName);
|
|
687
|
+
break;
|
|
688
|
+
case targetNames.has(colRef?.fields?.at(-1)?.String?.sval ?? ""):
|
|
689
|
+
nonNullableColumns.add(colRef?.fields?.at(-1)?.String?.sval ?? "");
|
|
690
|
+
break;
|
|
691
|
+
default:
|
|
692
|
+
nonNullableColumns.add(whereClauseName);
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (stmt.whereClause.BoolExpr?.boolop === BoolExprType.AND_EXPR) {
|
|
697
|
+
for (const arg of stmt.whereClause.BoolExpr.args) {
|
|
698
|
+
if (arg.NullTest?.nulltesttype === NullTestType.IS_NOT_NULL && arg.NullTest.arg?.ColumnRef?.fields) {
|
|
699
|
+
nonNullableColumns.add(concatStringNodes$1(arg.NullTest.arg.ColumnRef.fields));
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return nonNullableColumns;
|
|
705
|
+
}
|
|
706
|
+
function getNonNullableColumns(root) {
|
|
707
|
+
for (const stmt of root.stmts) {
|
|
708
|
+
if (stmt.stmt?.SelectStmt) {
|
|
709
|
+
return getNonNullableColumnsInSelectStmt(stmt.stmt.SelectStmt, root);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return /* @__PURE__ */ new Set();
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function getRelationsWithJoins(parsed) {
|
|
716
|
+
const results = /* @__PURE__ */ new Map();
|
|
717
|
+
const stmt = parsed.stmts[0];
|
|
718
|
+
if (stmt === void 0 || stmt?.stmt?.SelectStmt?.fromClause === void 0) {
|
|
719
|
+
return results;
|
|
720
|
+
}
|
|
721
|
+
for (const fromClause of stmt.stmt.SelectStmt.fromClause) {
|
|
722
|
+
if (fromClause.JoinExpr !== void 0) {
|
|
723
|
+
const { relName, joins } = recursiveTraverseJoins([], fromClause.JoinExpr);
|
|
724
|
+
results.set(relName, joins);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return results;
|
|
728
|
+
}
|
|
729
|
+
function recursiveGetJoinName(joinExpr) {
|
|
730
|
+
if (joinExpr.rarg?.JoinExpr !== void 0) {
|
|
731
|
+
return recursiveGetJoinName(joinExpr.rarg.JoinExpr);
|
|
732
|
+
}
|
|
733
|
+
return joinExpr.rarg?.RangeVar?.relname ?? joinExpr.rarg?.RangeSubselect?.alias?.aliasname;
|
|
734
|
+
}
|
|
735
|
+
function recursiveTraverseJoins(joins, joinExpr) {
|
|
736
|
+
const joinName = recursiveGetJoinName(joinExpr);
|
|
737
|
+
const aliasName = joinExpr.rarg?.RangeVar?.alias?.aliasname;
|
|
738
|
+
if (joinName === void 0) {
|
|
739
|
+
throw new Error("joinName is undefined");
|
|
740
|
+
}
|
|
741
|
+
const join = { type: joinExpr.jointype, name: joinName, alias: aliasName };
|
|
742
|
+
if (joinExpr.larg?.JoinExpr !== void 0) {
|
|
743
|
+
return recursiveTraverseJoins([join, ...joins], joinExpr.larg?.JoinExpr);
|
|
744
|
+
}
|
|
745
|
+
if (joinExpr.rarg?.JoinExpr !== void 0) {
|
|
746
|
+
return recursiveTraverseJoins([join, ...joins], joinExpr.rarg?.JoinExpr);
|
|
747
|
+
}
|
|
748
|
+
const relName = joinExpr.larg?.RangeVar?.relname ?? joinExpr.larg?.RangeFunction?.alias?.aliasname ?? joinExpr.rarg?.RangeVar?.relname ?? joinExpr.rarg?.RangeFunction?.alias?.aliasname;
|
|
749
|
+
if (relName === void 0) {
|
|
750
|
+
throw new Error("relName is undefined");
|
|
751
|
+
}
|
|
752
|
+
return { relName, joins: [join, ...joins] };
|
|
753
|
+
}
|
|
754
|
+
function flattenRelationsWithJoinsMap(relationsWithJoinsMap) {
|
|
755
|
+
const result = [];
|
|
756
|
+
relationsWithJoinsMap.forEach((joins, relName) => {
|
|
757
|
+
joins.forEach((join) => {
|
|
758
|
+
result.push({ relName, joinType: join.type, joinRelName: join.name, alias: join.alias });
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
return result;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function getASTDescription(params) {
|
|
765
|
+
const select = params.parsed.stmts[0]?.stmt?.SelectStmt;
|
|
766
|
+
if (select === void 0) {
|
|
767
|
+
return {
|
|
768
|
+
map: /* @__PURE__ */ new Map(),
|
|
769
|
+
meta: {
|
|
770
|
+
relations: [],
|
|
771
|
+
nonNullableColumns: /* @__PURE__ */ new Set()
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
const nonNullableColumns = getNonNullableColumns(params.parsed);
|
|
776
|
+
const relations = flattenRelationsWithJoinsMap(getRelationsWithJoins(params.parsed));
|
|
777
|
+
function getTypeByOid(oid) {
|
|
778
|
+
const name = params.pgTypes.get(oid)?.name;
|
|
779
|
+
if (name === void 0) {
|
|
780
|
+
return { isArray: false, override: false, value: "unknown" };
|
|
781
|
+
}
|
|
782
|
+
const { isArray, typeName } = name.startsWith("_") ? { isArray: true, typeName: name.slice(1) } : { isArray: false, typeName: name };
|
|
783
|
+
const resolvedTypeName = params.typesMap.get(typeName) ?? { override: false, value: "unknown" };
|
|
784
|
+
return {
|
|
785
|
+
isArray,
|
|
786
|
+
override: resolvedTypeName.override,
|
|
787
|
+
value: resolvedTypeName.value
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
const context = {
|
|
791
|
+
...params,
|
|
792
|
+
nonNullableColumns,
|
|
793
|
+
relations,
|
|
794
|
+
resolver: getSources({
|
|
795
|
+
relations,
|
|
796
|
+
select,
|
|
797
|
+
nonNullableColumns,
|
|
798
|
+
pgColsBySchemaAndTableName: params.pgColsBySchemaAndTableName
|
|
799
|
+
}),
|
|
800
|
+
select,
|
|
801
|
+
resolved: /* @__PURE__ */ new WeakMap(),
|
|
802
|
+
toTypeScriptType: (p) => {
|
|
803
|
+
if ("name" in p) {
|
|
804
|
+
return {
|
|
805
|
+
kind: "type",
|
|
806
|
+
value: params.typesMap.get(p.name)?.value ?? "unknown",
|
|
807
|
+
type: p.name
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
const pgType = params.pgTypes.get(p.oid);
|
|
811
|
+
const typeByOid = getTypeByOid(p.oid);
|
|
812
|
+
if (typeByOid.override) {
|
|
813
|
+
const baseType = {
|
|
814
|
+
kind: "type",
|
|
815
|
+
value: typeByOid.value,
|
|
816
|
+
type: pgType?.name ?? "unknown"
|
|
817
|
+
};
|
|
818
|
+
return typeByOid.isArray ? { kind: "array", value: baseType } : baseType;
|
|
819
|
+
}
|
|
820
|
+
const typeByBaseOid = checkSql_utils.fmap(p.baseOid, getTypeByOid);
|
|
821
|
+
if (typeByBaseOid?.override === true) {
|
|
822
|
+
const baseType = {
|
|
823
|
+
kind: "type",
|
|
824
|
+
value: typeByBaseOid.value,
|
|
825
|
+
type: params.pgTypes.get(p.baseOid)?.name ?? "unknown"
|
|
826
|
+
};
|
|
827
|
+
return typeByBaseOid.isArray ? { kind: "array", value: baseType } : baseType;
|
|
828
|
+
}
|
|
829
|
+
const getEnumByOid = (oid) => {
|
|
830
|
+
const pgEnum = params.pgEnums.get(oid);
|
|
831
|
+
if (pgEnum === void 0) {
|
|
832
|
+
return void 0;
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
kind: "union",
|
|
836
|
+
value: pgEnum.values.map((value2) => ({
|
|
837
|
+
kind: "type",
|
|
838
|
+
value: `'${value2}'`,
|
|
839
|
+
type: pgEnum.name
|
|
840
|
+
}))
|
|
841
|
+
};
|
|
842
|
+
};
|
|
843
|
+
const valueAsEnum = (() => {
|
|
844
|
+
const enumType = getEnumByOid(p.oid);
|
|
845
|
+
if (enumType !== void 0) {
|
|
846
|
+
return enumType;
|
|
847
|
+
}
|
|
848
|
+
if (pgType?.typelem !== void 0 && pgType.typelem !== 0) {
|
|
849
|
+
const arrayEnumValue = getEnumByOid(pgType.typelem);
|
|
850
|
+
if (arrayEnumValue !== void 0) {
|
|
851
|
+
return {
|
|
852
|
+
kind: "array",
|
|
853
|
+
value: arrayEnumValue
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return void 0;
|
|
858
|
+
})();
|
|
859
|
+
if (valueAsEnum !== void 0) {
|
|
860
|
+
return valueAsEnum;
|
|
861
|
+
}
|
|
862
|
+
const { isArray, value } = typeByBaseOid ?? typeByOid;
|
|
863
|
+
const type = {
|
|
864
|
+
kind: "type",
|
|
865
|
+
value,
|
|
866
|
+
type: pgType?.name ?? "unknown"
|
|
867
|
+
};
|
|
868
|
+
if (p.baseOid !== null) {
|
|
869
|
+
type.base = params.pgTypes.get(p.baseOid)?.name;
|
|
870
|
+
}
|
|
871
|
+
return isArray ? { kind: "array", value: type } : type;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
const targetLists = [select.targetList, select.larg?.targetList, select.rarg?.targetList].filter(
|
|
875
|
+
(x) => x !== void 0
|
|
876
|
+
);
|
|
877
|
+
const resolvedColumnsList = [];
|
|
878
|
+
for (const [targetListIdx, targetList] of targetLists.entries()) {
|
|
879
|
+
resolvedColumnsList[targetListIdx] = targetList.map((target) => {
|
|
880
|
+
const described = getDescribedNode({
|
|
881
|
+
alias: void 0,
|
|
882
|
+
node: target,
|
|
883
|
+
context
|
|
884
|
+
});
|
|
885
|
+
if (described.length === 0) {
|
|
886
|
+
return [void 0];
|
|
887
|
+
}
|
|
888
|
+
return described;
|
|
889
|
+
}).flat();
|
|
890
|
+
}
|
|
891
|
+
const columnsLength = resolvedColumnsList.reduce((acc, x) => Math.max(acc, x.length), 0);
|
|
892
|
+
const final = /* @__PURE__ */ new Map();
|
|
893
|
+
for (let i = 0; i < columnsLength; i++) {
|
|
894
|
+
const result = mergeColumns(resolvedColumnsList.map((x) => x[i]));
|
|
895
|
+
final.set(i, result);
|
|
896
|
+
}
|
|
897
|
+
return {
|
|
898
|
+
map: final,
|
|
899
|
+
meta: {
|
|
900
|
+
relations,
|
|
901
|
+
nonNullableColumns
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
function mergeColumns(columns) {
|
|
906
|
+
const definedColumns = columns.filter((x) => x !== void 0);
|
|
907
|
+
if (definedColumns.length === 0) {
|
|
908
|
+
return void 0;
|
|
909
|
+
}
|
|
910
|
+
const name = definedColumns[0].name;
|
|
911
|
+
const type = mergeDescribedColumnTypes(definedColumns.map((x) => x.type));
|
|
912
|
+
return { name, type };
|
|
913
|
+
}
|
|
914
|
+
function getDescribedNode(params) {
|
|
915
|
+
const { alias, node, context } = params;
|
|
916
|
+
if (node.ResTarget !== void 0) {
|
|
917
|
+
return getDescribedResTarget({ node: node.ResTarget, context });
|
|
918
|
+
}
|
|
919
|
+
if (node.A_Const !== void 0) {
|
|
920
|
+
return getDescribedAConst({ alias, node: node.A_Const, context });
|
|
921
|
+
}
|
|
922
|
+
if (node.ColumnRef !== void 0) {
|
|
923
|
+
return getDescribedColumnRef({ alias, node: node.ColumnRef, context });
|
|
924
|
+
}
|
|
925
|
+
if (node.FuncCall !== void 0) {
|
|
926
|
+
return getDescribedFuncCall({ alias, node: node.FuncCall, context });
|
|
927
|
+
}
|
|
928
|
+
if (node.TypeCast !== void 0) {
|
|
929
|
+
return getDescribedTypeCast({ alias, node: node.TypeCast, context });
|
|
930
|
+
}
|
|
931
|
+
if (node.A_ArrayExpr !== void 0) {
|
|
932
|
+
return getDescribedArrayExpr({ alias, node: node.A_ArrayExpr, context });
|
|
933
|
+
}
|
|
934
|
+
if (node.CoalesceExpr !== void 0) {
|
|
935
|
+
return getDescribedCoalesceExpr({ alias, node: node.CoalesceExpr, context });
|
|
936
|
+
}
|
|
937
|
+
if (node.SubLink !== void 0) {
|
|
938
|
+
return getDescribedSubLink({ alias, node: node.SubLink, context });
|
|
939
|
+
}
|
|
940
|
+
if (node.BoolExpr !== void 0) {
|
|
941
|
+
return getDescribedBoolExpr({ alias, node: node.BoolExpr, context });
|
|
942
|
+
}
|
|
943
|
+
if (node.CaseExpr !== void 0) {
|
|
944
|
+
return getDescribedCaseExpr({ alias, node: node.CaseExpr, context });
|
|
945
|
+
}
|
|
946
|
+
if (node.NullTest !== void 0) {
|
|
947
|
+
return getDescribedNullTest({ alias, node: node.NullTest, context });
|
|
948
|
+
}
|
|
949
|
+
if (node.A_Expr !== void 0) {
|
|
950
|
+
return getDescribedAExpr({ alias, node: node.A_Expr, context });
|
|
951
|
+
}
|
|
952
|
+
if (node.SelectStmt !== void 0) {
|
|
953
|
+
return getDescribedSelectStmt({ alias, node: node.SelectStmt, context });
|
|
954
|
+
}
|
|
955
|
+
return [];
|
|
956
|
+
}
|
|
957
|
+
function getDescribedAExpr({
|
|
958
|
+
alias,
|
|
959
|
+
node,
|
|
960
|
+
context
|
|
961
|
+
}) {
|
|
962
|
+
const name = alias ?? "?column?";
|
|
963
|
+
if (node.lexpr === void 0 && node.rexpr !== void 0) {
|
|
964
|
+
const described = getDescribedNode({ alias, node: node.rexpr, context }).at(0);
|
|
965
|
+
const type2 = checkSql_utils.fmap(described, (x) => getBaseType(x.type));
|
|
966
|
+
if (type2 === null) return [];
|
|
967
|
+
return [{ name, type: type2 }];
|
|
968
|
+
}
|
|
969
|
+
if (node.lexpr === void 0 || node.rexpr === void 0) {
|
|
970
|
+
return [];
|
|
971
|
+
}
|
|
972
|
+
const getResolvedNullableValueOrNull = (node2) => {
|
|
973
|
+
const column = getDescribedNode({ alias: void 0, node: node2, context }).at(0);
|
|
974
|
+
if (column === void 0) return null;
|
|
975
|
+
const getFromType = (type2) => {
|
|
976
|
+
switch (true) {
|
|
977
|
+
case type2.kind === "type":
|
|
978
|
+
return { value: type2.base ?? type2.type, array: false, nullable: false };
|
|
979
|
+
case (type2.kind === "literal" && type2.base.kind === "type"):
|
|
980
|
+
return { value: type2.base.type, array: false, nullable: false };
|
|
981
|
+
case (type2.kind === "union" && type2.value.every((x) => x.kind === "literal")): {
|
|
982
|
+
const resolved = getFromType(type2.value[0].base);
|
|
983
|
+
if (resolved === null) return null;
|
|
984
|
+
return { value: resolved.value, nullable: false, array: false };
|
|
985
|
+
}
|
|
986
|
+
case (type2.kind === "union" && isTuple(type2.value)): {
|
|
987
|
+
let nullable = false;
|
|
988
|
+
let value = void 0;
|
|
989
|
+
for (const valueType of type2.value) {
|
|
990
|
+
if (valueType.kind !== "type") return null;
|
|
991
|
+
if (valueType.value === "null") nullable = true;
|
|
992
|
+
if (valueType.value !== "null") value = valueType.type;
|
|
993
|
+
}
|
|
994
|
+
if (value === void 0) return null;
|
|
995
|
+
return { value, nullable, array: false };
|
|
996
|
+
}
|
|
997
|
+
case type2.kind === "object":
|
|
998
|
+
return { value: "jsonb", array: false, nullable: false };
|
|
999
|
+
default:
|
|
1000
|
+
return null;
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
if (column.type.kind === "array") {
|
|
1004
|
+
const resolved = getFromType(column.type.value);
|
|
1005
|
+
if (!resolved) return null;
|
|
1006
|
+
return { value: resolved.value, nullable: resolved.nullable, array: true };
|
|
1007
|
+
}
|
|
1008
|
+
return getFromType(column.type);
|
|
1009
|
+
};
|
|
1010
|
+
const lnode = getResolvedNullableValueOrNull(node.lexpr);
|
|
1011
|
+
const rnode = getResolvedNullableValueOrNull(node.rexpr);
|
|
1012
|
+
const operator = concatStringNodes(node.name);
|
|
1013
|
+
if (lnode === null || rnode === null) {
|
|
1014
|
+
return [];
|
|
1015
|
+
}
|
|
1016
|
+
const downcast = () => {
|
|
1017
|
+
const left = lnode.array ? `_${lnode.value}` : lnode.value;
|
|
1018
|
+
const right = rnode.array ? `_${rnode.value}` : rnode.value;
|
|
1019
|
+
const overrides = {
|
|
1020
|
+
"int4 ^ int4": ["float8", "^", "float8"]
|
|
1021
|
+
};
|
|
1022
|
+
if (overrides[`${left} ${operator} ${right}`]) {
|
|
1023
|
+
return overrides[`${left} ${operator} ${right}`];
|
|
1024
|
+
}
|
|
1025
|
+
const adjust = (value) => value === "varchar" ? "text" : value;
|
|
1026
|
+
return [adjust(left), operator, adjust(right)];
|
|
1027
|
+
};
|
|
1028
|
+
const getNullable = () => {
|
|
1029
|
+
if (context.nonNullableColumns.has(name)) {
|
|
1030
|
+
return false;
|
|
1031
|
+
}
|
|
1032
|
+
if (lnode.nullable || rnode.nullable) {
|
|
1033
|
+
return true;
|
|
1034
|
+
}
|
|
1035
|
+
const operatorForcesNullable = ["->>", "#>>"].includes(operator) && hasColumnReference(node.lexpr);
|
|
1036
|
+
return operatorForcesNullable;
|
|
1037
|
+
};
|
|
1038
|
+
const getType = () => {
|
|
1039
|
+
const nullable = getNullable();
|
|
1040
|
+
const [dleft, doperator, dright] = downcast();
|
|
1041
|
+
const type2 = context.typeExprMap.get(dleft)?.get(doperator)?.get(dright) ?? context.typeExprMap.get("anycompatiblearray")?.get(operator)?.get("anycompatiblearray") ?? context.typeExprMap.get("anyarray")?.get(operator)?.get("anyarray") ?? context.typeExprMap.get(lnode.value)?.get(operator)?.values().next().value;
|
|
1042
|
+
if (type2 === void 0) {
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
if (type2 === "anycompatiblearray") {
|
|
1046
|
+
return {
|
|
1047
|
+
kind: "array",
|
|
1048
|
+
value: resolveType({
|
|
1049
|
+
context,
|
|
1050
|
+
nullable,
|
|
1051
|
+
type: context.toTypeScriptType({ name: lnode.value })
|
|
1052
|
+
})
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
return resolveType({
|
|
1056
|
+
context,
|
|
1057
|
+
nullable,
|
|
1058
|
+
type: context.toTypeScriptType({ name: type2 })
|
|
1059
|
+
});
|
|
1060
|
+
};
|
|
1061
|
+
const type = getType();
|
|
1062
|
+
if (type === void 0) {
|
|
1063
|
+
return [];
|
|
1064
|
+
}
|
|
1065
|
+
return [{ name, type }];
|
|
1066
|
+
}
|
|
1067
|
+
function getDescribedNullTest({
|
|
1068
|
+
alias,
|
|
1069
|
+
context
|
|
1070
|
+
}) {
|
|
1071
|
+
return [
|
|
1072
|
+
{
|
|
1073
|
+
name: alias ?? "?column?",
|
|
1074
|
+
type: resolveType({
|
|
1075
|
+
context,
|
|
1076
|
+
nullable: false,
|
|
1077
|
+
type: context.toTypeScriptType({ name: "bool" })
|
|
1078
|
+
})
|
|
1079
|
+
}
|
|
1080
|
+
];
|
|
1081
|
+
}
|
|
1082
|
+
function getDescribedCaseExpr({
|
|
1083
|
+
alias,
|
|
1084
|
+
node,
|
|
1085
|
+
context
|
|
1086
|
+
}) {
|
|
1087
|
+
const results = [];
|
|
1088
|
+
for (const arg of node.args) {
|
|
1089
|
+
if (arg.CaseWhen?.result !== void 0) {
|
|
1090
|
+
results.push(getDescribedNode({ alias: void 0, node: arg.CaseWhen.result, context }));
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
results.push(
|
|
1094
|
+
node.defresult !== void 0 ? getDescribedNode({ alias: void 0, node: node.defresult, context }) : [{ name: "?column?", type: context.toTypeScriptType({ name: "null" }) }]
|
|
1095
|
+
);
|
|
1096
|
+
const types = results.flat().map((x) => x.type);
|
|
1097
|
+
const literalsOnly = types.some((x) => x.kind !== "literal" && x.value !== "null");
|
|
1098
|
+
const value = mergeDescribedColumnTypes(literalsOnly ? types.map(getBaseType) : types);
|
|
1099
|
+
return [
|
|
1100
|
+
{
|
|
1101
|
+
name: alias ?? "case",
|
|
1102
|
+
type: value
|
|
1103
|
+
}
|
|
1104
|
+
];
|
|
1105
|
+
}
|
|
1106
|
+
function getBaseType(type) {
|
|
1107
|
+
switch (type.kind) {
|
|
1108
|
+
case "object":
|
|
1109
|
+
case "union":
|
|
1110
|
+
case "array":
|
|
1111
|
+
case "type":
|
|
1112
|
+
return type;
|
|
1113
|
+
case "literal":
|
|
1114
|
+
return type.base;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function getDescribedBoolExpr({
|
|
1118
|
+
alias,
|
|
1119
|
+
context
|
|
1120
|
+
}) {
|
|
1121
|
+
return [
|
|
1122
|
+
{
|
|
1123
|
+
name: alias ?? "?column?",
|
|
1124
|
+
type: resolveType({
|
|
1125
|
+
context,
|
|
1126
|
+
nullable: false,
|
|
1127
|
+
type: context.toTypeScriptType({ name: "bool" })
|
|
1128
|
+
})
|
|
1129
|
+
}
|
|
1130
|
+
];
|
|
1131
|
+
}
|
|
1132
|
+
function getDescribedSubLink({
|
|
1133
|
+
alias,
|
|
1134
|
+
context,
|
|
1135
|
+
node
|
|
1136
|
+
}) {
|
|
1137
|
+
const getSubLinkType = () => {
|
|
1138
|
+
if (node.subLinkType === SubLinkType.EXISTS_SUBLINK) {
|
|
1139
|
+
return context.toTypeScriptType({ name: "bool" });
|
|
1140
|
+
}
|
|
1141
|
+
if (node.subLinkType === SubLinkType.EXPR_SUBLINK) {
|
|
1142
|
+
const described = node.subselect?.SelectStmt ? getDescribedNode({
|
|
1143
|
+
alias: void 0,
|
|
1144
|
+
node: { SelectStmt: node.subselect.SelectStmt },
|
|
1145
|
+
context
|
|
1146
|
+
}) : [];
|
|
1147
|
+
return described.length > 0 ? described[0].type : context.toTypeScriptType({ name: "unknown" });
|
|
1148
|
+
}
|
|
1149
|
+
return context.toTypeScriptType({ name: "unknown" });
|
|
1150
|
+
};
|
|
1151
|
+
return [
|
|
1152
|
+
{
|
|
1153
|
+
name: alias ?? "exists",
|
|
1154
|
+
type: resolveType({
|
|
1155
|
+
context,
|
|
1156
|
+
nullable: false,
|
|
1157
|
+
type: getSubLinkType()
|
|
1158
|
+
})
|
|
1159
|
+
}
|
|
1160
|
+
];
|
|
1161
|
+
}
|
|
1162
|
+
function getDescribedSelectStmt({
|
|
1163
|
+
alias,
|
|
1164
|
+
context,
|
|
1165
|
+
node
|
|
1166
|
+
}) {
|
|
1167
|
+
const subParsed = {
|
|
1168
|
+
version: 0,
|
|
1169
|
+
stmts: [{ stmt: { SelectStmt: node }, stmtLocation: 0, stmtLen: 0 }]
|
|
1170
|
+
};
|
|
1171
|
+
const subDescription = getASTDescription({
|
|
1172
|
+
parsed: subParsed,
|
|
1173
|
+
typesMap: context.typesMap,
|
|
1174
|
+
typeExprMap: context.typeExprMap,
|
|
1175
|
+
overridenColumnTypesMap: context.overridenColumnTypesMap,
|
|
1176
|
+
pgColsBySchemaAndTableName: context.pgColsBySchemaAndTableName,
|
|
1177
|
+
pgTypes: context.pgTypes,
|
|
1178
|
+
pgEnums: context.pgEnums,
|
|
1179
|
+
pgFns: context.pgFns,
|
|
1180
|
+
fieldTransform: context.fieldTransform
|
|
1181
|
+
});
|
|
1182
|
+
const firstColumn = subDescription.map.get(0);
|
|
1183
|
+
if (firstColumn) {
|
|
1184
|
+
return [
|
|
1185
|
+
{
|
|
1186
|
+
name: alias ?? firstColumn.name,
|
|
1187
|
+
type: firstColumn.type
|
|
1188
|
+
}
|
|
1189
|
+
];
|
|
1190
|
+
}
|
|
1191
|
+
return [];
|
|
1192
|
+
}
|
|
1193
|
+
function getDescribedCoalesceExpr({
|
|
1194
|
+
alias,
|
|
1195
|
+
context,
|
|
1196
|
+
node
|
|
1197
|
+
}) {
|
|
1198
|
+
const firstArg = node.args.at(0);
|
|
1199
|
+
const unknownCoalesce = {
|
|
1200
|
+
name: alias ?? "coalesce",
|
|
1201
|
+
type: context.toTypeScriptType({ name: "unknown" })
|
|
1202
|
+
};
|
|
1203
|
+
if (firstArg === void 0) {
|
|
1204
|
+
return [unknownCoalesce];
|
|
1205
|
+
}
|
|
1206
|
+
const type = getDescribedNode({ alias: void 0, node: firstArg, context }).map((x) => asNonNullableType(x.type)).at(0);
|
|
1207
|
+
if (type === void 0) {
|
|
1208
|
+
return [];
|
|
1209
|
+
}
|
|
1210
|
+
return [
|
|
1211
|
+
{
|
|
1212
|
+
name: alias ?? "coalesce",
|
|
1213
|
+
type
|
|
1214
|
+
}
|
|
1215
|
+
];
|
|
1216
|
+
}
|
|
1217
|
+
function getDescribedArrayExpr({
|
|
1218
|
+
alias,
|
|
1219
|
+
context,
|
|
1220
|
+
node
|
|
1221
|
+
}) {
|
|
1222
|
+
const types = mergeDescribedColumnTypes(
|
|
1223
|
+
node.elements.flatMap((node2) => getDescribedNode({ alias: void 0, node: node2, context })).map((x) => x.type)
|
|
1224
|
+
);
|
|
1225
|
+
return [
|
|
1226
|
+
{
|
|
1227
|
+
name: alias ?? "?column?",
|
|
1228
|
+
type: {
|
|
1229
|
+
kind: "array",
|
|
1230
|
+
value: types
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
];
|
|
1234
|
+
}
|
|
1235
|
+
function mergeDescribedColumnTypes(types) {
|
|
1236
|
+
const result = [];
|
|
1237
|
+
const seenSymbols = /* @__PURE__ */ new Set();
|
|
1238
|
+
function processType(type) {
|
|
1239
|
+
switch (type.kind) {
|
|
1240
|
+
case "union":
|
|
1241
|
+
type.value.forEach((subtype) => processType(subtype));
|
|
1242
|
+
break;
|
|
1243
|
+
case "type":
|
|
1244
|
+
case "literal":
|
|
1245
|
+
if (!seenSymbols.has(type.value)) {
|
|
1246
|
+
seenSymbols.add(type.value);
|
|
1247
|
+
result.push(type);
|
|
1248
|
+
}
|
|
1249
|
+
break;
|
|
1250
|
+
case "object":
|
|
1251
|
+
case "array":
|
|
1252
|
+
result.push(type);
|
|
1253
|
+
break;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
for (const t of types) {
|
|
1257
|
+
processType(t);
|
|
1258
|
+
}
|
|
1259
|
+
if (!seenSymbols.has("boolean") && seenSymbols.has("true") && seenSymbols.has("false")) {
|
|
1260
|
+
seenSymbols.add("boolean");
|
|
1261
|
+
result.push({ kind: "type", value: "boolean", type: "bool" });
|
|
1262
|
+
}
|
|
1263
|
+
if (seenSymbols.has("boolean") && (seenSymbols.has("true") || seenSymbols.has("false"))) {
|
|
1264
|
+
const filtered = result.filter(
|
|
1265
|
+
(t) => !(t.kind === "literal" && (t.value === "true" || t.value === "false"))
|
|
1266
|
+
);
|
|
1267
|
+
return isSingleCell(filtered) ? filtered[0] : { kind: "union", value: filtered };
|
|
1268
|
+
}
|
|
1269
|
+
return isSingleCell(result) ? result[0] : { kind: "union", value: result };
|
|
1270
|
+
}
|
|
1271
|
+
function getDescribedTypeCast({
|
|
1272
|
+
alias,
|
|
1273
|
+
context,
|
|
1274
|
+
node
|
|
1275
|
+
}) {
|
|
1276
|
+
let typeName = node.typeName?.names.at(-1)?.String?.sval;
|
|
1277
|
+
if (typeName === "int") {
|
|
1278
|
+
typeName = "int4";
|
|
1279
|
+
}
|
|
1280
|
+
if (typeName === void 0 || node.arg === void 0) {
|
|
1281
|
+
return [];
|
|
1282
|
+
}
|
|
1283
|
+
const type = context.toTypeScriptType({ name: typeName });
|
|
1284
|
+
const innerDescribed = getDescribedNode({ alias, node: node.arg, context }).at(0);
|
|
1285
|
+
const nullable = checkSql_utils.fmap(innerDescribed, (x) => isDescribedColumnNullable(x.type)) ?? true;
|
|
1286
|
+
switch (true) {
|
|
1287
|
+
case node.arg.FuncCall !== void 0: {
|
|
1288
|
+
return [
|
|
1289
|
+
{
|
|
1290
|
+
name: alias ?? node.arg.FuncCall?.funcname.at(-1)?.String?.sval ?? "?column?",
|
|
1291
|
+
type: resolveType({ context, nullable, type })
|
|
1292
|
+
}
|
|
1293
|
+
];
|
|
1294
|
+
}
|
|
1295
|
+
case node.arg.ColumnRef !== void 0: {
|
|
1296
|
+
return [
|
|
1297
|
+
{
|
|
1298
|
+
name: alias ?? concatStringNodes(node.arg.ColumnRef.fields),
|
|
1299
|
+
type: resolveType({ context, nullable, type })
|
|
1300
|
+
}
|
|
1301
|
+
];
|
|
1302
|
+
}
|
|
1303
|
+
default: {
|
|
1304
|
+
return [
|
|
1305
|
+
{
|
|
1306
|
+
name: alias ?? typeName ?? "?column?",
|
|
1307
|
+
type: resolveType({ context, nullable, type })
|
|
1308
|
+
}
|
|
1309
|
+
];
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
function getDescribedResTarget(params) {
|
|
1314
|
+
const { node, context } = params;
|
|
1315
|
+
if (node.val === void 0) {
|
|
1316
|
+
return [];
|
|
1317
|
+
}
|
|
1318
|
+
return getDescribedNode({
|
|
1319
|
+
alias: node.name,
|
|
1320
|
+
context,
|
|
1321
|
+
node: node.val
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
function getDescribedFuncCall(params) {
|
|
1325
|
+
const functionName = params.node.funcname.at(-1)?.String?.sval;
|
|
1326
|
+
if (functionName === void 0) {
|
|
1327
|
+
return [];
|
|
1328
|
+
}
|
|
1329
|
+
switch (true) {
|
|
1330
|
+
case functionName === "json_build_object":
|
|
1331
|
+
case functionName === "jsonb_build_object":
|
|
1332
|
+
return getDescribedJsonBuildObjectFunCall(params);
|
|
1333
|
+
case functionName === "json_agg":
|
|
1334
|
+
case functionName === "jsonb_agg":
|
|
1335
|
+
return getDescribedJsonAggFunCall(params);
|
|
1336
|
+
case functionName === "array_agg":
|
|
1337
|
+
return getDescribedArrayAggFunCall(params);
|
|
1338
|
+
case functionName === "unnest":
|
|
1339
|
+
return getDescribedUnnestFunCall(params);
|
|
1340
|
+
default:
|
|
1341
|
+
return getDescribedFuncCallByPgFn(params);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
function getDescribedUnnestFunCall({
|
|
1345
|
+
alias,
|
|
1346
|
+
context,
|
|
1347
|
+
node
|
|
1348
|
+
}) {
|
|
1349
|
+
const functionName = node.funcname.at(-1)?.String?.sval ?? "";
|
|
1350
|
+
const name = alias ?? functionName;
|
|
1351
|
+
const firstArg = node.args?.at(0);
|
|
1352
|
+
if (firstArg === void 0) {
|
|
1353
|
+
return [{ name, type: context.toTypeScriptType({ name: "unknown" }) }];
|
|
1354
|
+
}
|
|
1355
|
+
const described = getDescribedNode({ alias: void 0, node: firstArg, context }).at(0);
|
|
1356
|
+
if (described === void 0) {
|
|
1357
|
+
return [{ name, type: context.toTypeScriptType({ name: "unknown" }) }];
|
|
1358
|
+
}
|
|
1359
|
+
function unwrap(type) {
|
|
1360
|
+
if (type.kind === "array") {
|
|
1361
|
+
return unwrap(type.value);
|
|
1362
|
+
}
|
|
1363
|
+
if (type.kind === "union") {
|
|
1364
|
+
const hasArray = type.value.some((t) => t.kind === "array");
|
|
1365
|
+
const unwrapped = type.value.filter((t) => !hasArray || !(t.kind === "type" && t.value === "null")).map(unwrap);
|
|
1366
|
+
return mergeDescribedColumnTypes(unwrapped);
|
|
1367
|
+
}
|
|
1368
|
+
if (type.kind === "type" && type.type.startsWith("_")) {
|
|
1369
|
+
return { ...type, type: type.type.slice(1) };
|
|
1370
|
+
}
|
|
1371
|
+
return type;
|
|
1372
|
+
}
|
|
1373
|
+
return [{ name, type: unwrap(described.type) }];
|
|
1374
|
+
}
|
|
1375
|
+
function getDescribedFuncCallByPgFn({
|
|
1376
|
+
alias,
|
|
1377
|
+
context,
|
|
1378
|
+
node
|
|
1379
|
+
}) {
|
|
1380
|
+
const functionName = node.funcname.at(-1)?.String?.sval;
|
|
1381
|
+
const name = alias ?? functionName ?? "?column?";
|
|
1382
|
+
const args = (node.args ?? []).flatMap((node2) => {
|
|
1383
|
+
const described = getDescribedNode({ alias: void 0, node: node2, context }).at(0);
|
|
1384
|
+
if (described?.type.kind === "type") {
|
|
1385
|
+
return [described.type.value];
|
|
1386
|
+
}
|
|
1387
|
+
return [];
|
|
1388
|
+
});
|
|
1389
|
+
if (functionName === void 0) {
|
|
1390
|
+
return [{ name, type: context.toTypeScriptType({ name: "unknown" }) }];
|
|
1391
|
+
}
|
|
1392
|
+
const pgFnValue = args.length === 0 ? context.pgFns.get(functionName) ?? context.pgFns.get(`${functionName}(string)`) : context.pgFns.get(`${functionName}(${args.join(", ")})`) ?? context.pgFns.get(`${functionName}(any)`) ?? context.pgFns.get(`${functionName}(unknown)`);
|
|
1393
|
+
const type = resolveType({
|
|
1394
|
+
context,
|
|
1395
|
+
nullable: !context.nonNullableColumns.has(name),
|
|
1396
|
+
type: { kind: "type", value: pgFnValue?.ts ?? "unknown", type: pgFnValue?.pg ?? "unknown" }
|
|
1397
|
+
});
|
|
1398
|
+
return [{ name, type }];
|
|
1399
|
+
}
|
|
1400
|
+
function getDescribedArrayAggFunCall({
|
|
1401
|
+
alias,
|
|
1402
|
+
context,
|
|
1403
|
+
node
|
|
1404
|
+
}) {
|
|
1405
|
+
const name = alias ?? concatStringNodes(node.funcname);
|
|
1406
|
+
const firstArg = checkSql_utils.fmap(node.args?.at(0), (node2) => {
|
|
1407
|
+
return {
|
|
1408
|
+
node: node2,
|
|
1409
|
+
described: getDescribedNode({ alias: void 0, node: node2, context })
|
|
1410
|
+
};
|
|
1411
|
+
});
|
|
1412
|
+
if (firstArg === null) {
|
|
1413
|
+
return [
|
|
1414
|
+
{
|
|
1415
|
+
name,
|
|
1416
|
+
type: context.toTypeScriptType({ name: "unknown" })
|
|
1417
|
+
}
|
|
1418
|
+
];
|
|
1419
|
+
}
|
|
1420
|
+
const isSourceRef = context.resolver.sources.get(concatStringNodes(firstArg.node.ColumnRef?.fields)) !== void 0;
|
|
1421
|
+
const type = resolveType({
|
|
1422
|
+
context,
|
|
1423
|
+
nullable: !context.nonNullableColumns.has(name),
|
|
1424
|
+
type: {
|
|
1425
|
+
kind: "array",
|
|
1426
|
+
value: isSourceRef || firstArg.described.length > 1 ? {
|
|
1427
|
+
kind: "object",
|
|
1428
|
+
value: firstArg.described.map((x) => [x.name, x.type])
|
|
1429
|
+
} : firstArg.described[0].type
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
return [{ name, type }];
|
|
1433
|
+
}
|
|
1434
|
+
function getDescribedJsonAggFunCall({
|
|
1435
|
+
alias,
|
|
1436
|
+
context,
|
|
1437
|
+
node
|
|
1438
|
+
}) {
|
|
1439
|
+
const name = alias ?? concatStringNodes(node.funcname);
|
|
1440
|
+
if (node.args === void 0 || !isSingleCell(node.args)) {
|
|
1441
|
+
return [];
|
|
1442
|
+
}
|
|
1443
|
+
const argNode = node.args[0];
|
|
1444
|
+
const cellType = getDescribedNode({ alias: void 0, node: argNode, context });
|
|
1445
|
+
if (cellType.length === 0) {
|
|
1446
|
+
return [];
|
|
1447
|
+
}
|
|
1448
|
+
const isSourceRef = context.resolver.sources.get(concatStringNodes(argNode.ColumnRef?.fields)) !== void 0;
|
|
1449
|
+
const type = resolveType({
|
|
1450
|
+
context,
|
|
1451
|
+
nullable: !context.nonNullableColumns.has(name),
|
|
1452
|
+
type: {
|
|
1453
|
+
kind: "array",
|
|
1454
|
+
value: isSourceRef || cellType.length > 1 ? {
|
|
1455
|
+
kind: "object",
|
|
1456
|
+
value: cellType.map((x) => [x.name, x.type])
|
|
1457
|
+
} : cellType[0].type
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
return [{ name, type }];
|
|
1461
|
+
}
|
|
1462
|
+
function getDescribedJsonBuildObjectFunCall({
|
|
1463
|
+
alias,
|
|
1464
|
+
context,
|
|
1465
|
+
node
|
|
1466
|
+
}) {
|
|
1467
|
+
const functionName = node.funcname.at(-1)?.String?.sval ?? "";
|
|
1468
|
+
const name = alias ?? functionName;
|
|
1469
|
+
const unknownDescribedColumn = {
|
|
1470
|
+
name,
|
|
1471
|
+
type: context.toTypeScriptType({ name: "unknown" })
|
|
1472
|
+
};
|
|
1473
|
+
if (node.args === void 0) {
|
|
1474
|
+
return [unknownDescribedColumn];
|
|
1475
|
+
}
|
|
1476
|
+
const describedColumn = { kind: "object", value: [] };
|
|
1477
|
+
for (const [idx, arg] of node.args.entries()) {
|
|
1478
|
+
if (idx % 2 === 1) {
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
const valueNode = node.args.at(idx + 1);
|
|
1482
|
+
if (valueNode === void 0) {
|
|
1483
|
+
throw new Error(checkSql_utils.normalizeIndent`
|
|
1484
|
+
argument list must have even number of elements
|
|
1485
|
+
Hint: The arguments of ${functionName}() must consist of alternating keys and values.
|
|
1486
|
+
`);
|
|
1487
|
+
}
|
|
1488
|
+
const alias2 = arg.A_Const?.sval?.sval;
|
|
1489
|
+
const type = checkSql_utils.fmap(node.args[idx + 1], (node2) => {
|
|
1490
|
+
return getDescribedNode({ alias: alias2, node: node2, context }).at(0)?.type ?? null;
|
|
1491
|
+
});
|
|
1492
|
+
if (alias2 === void 0 || type === null) {
|
|
1493
|
+
return [unknownDescribedColumn];
|
|
1494
|
+
}
|
|
1495
|
+
const transformedKey = checkSql_utils.toCase(alias2, context.fieldTransform);
|
|
1496
|
+
describedColumn.value.push([transformedKey, resolveType({ context, nullable: false, type })]);
|
|
1497
|
+
}
|
|
1498
|
+
return [
|
|
1499
|
+
{
|
|
1500
|
+
name,
|
|
1501
|
+
type: describedColumn
|
|
1502
|
+
}
|
|
1503
|
+
];
|
|
1504
|
+
}
|
|
1505
|
+
function getColumnRefOrigins({
|
|
1506
|
+
context,
|
|
1507
|
+
node
|
|
1508
|
+
}) {
|
|
1509
|
+
if (isColumnTableStarRef(node.fields)) {
|
|
1510
|
+
const source = node.fields[0].String.sval;
|
|
1511
|
+
return (
|
|
1512
|
+
// lookup in cte
|
|
1513
|
+
context.select.withClause?.ctes.find((cte) => cte.CommonTableExpr?.ctename === source)?.CommonTableExpr?.ctequery?.SelectStmt?.targetList ?? // lookup in subselect
|
|
1514
|
+
context.select.fromClause?.map((from) => from.RangeSubselect).find((subselect) => subselect?.alias?.aliasname === source)?.subquery?.SelectStmt?.targetList
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
if (isColumnTableColumnRef(node.fields)) {
|
|
1518
|
+
const source = node.fields[0].String.sval;
|
|
1519
|
+
const column = node.fields[1].String.sval;
|
|
1520
|
+
const origin = (
|
|
1521
|
+
// lookup in cte
|
|
1522
|
+
context.select.withClause?.ctes.find((cte) => cte.CommonTableExpr?.ctename === source)?.CommonTableExpr?.ctequery?.SelectStmt?.targetList?.find(
|
|
1523
|
+
(x) => x.ResTarget?.name === column
|
|
1524
|
+
) ?? // lookup in subselect
|
|
1525
|
+
context.select.fromClause?.map((from) => from.RangeSubselect).find((subselect) => subselect?.alias?.aliasname === source)?.subquery?.SelectStmt?.targetList?.find((x) => x.ResTarget?.name === column)
|
|
1526
|
+
);
|
|
1527
|
+
if (!origin) return void 0;
|
|
1528
|
+
return [origin];
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
function getContextForColumnRef(context, node) {
|
|
1532
|
+
if (isColumnTableColumnRef(node.fields) || isColumnTableStarRef(node.fields)) {
|
|
1533
|
+
const sourceName = node.fields[0].String.sval;
|
|
1534
|
+
const source = context.resolver.sources.get(sourceName);
|
|
1535
|
+
if (source?.kind === "cte") {
|
|
1536
|
+
return { ...context, resolver: source.sources };
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
return context;
|
|
1540
|
+
}
|
|
1541
|
+
function getDescribedColumnRef({
|
|
1542
|
+
alias,
|
|
1543
|
+
context,
|
|
1544
|
+
node
|
|
1545
|
+
}) {
|
|
1546
|
+
const definitionNodes = getColumnRefOrigins({ context, node });
|
|
1547
|
+
if (definitionNodes) {
|
|
1548
|
+
const defContext = getContextForColumnRef(context, node);
|
|
1549
|
+
return definitionNodes.flatMap(
|
|
1550
|
+
(node2) => getDescribedNode({ alias, node: node2, context: defContext })
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
return getDescribedColumnRefFromSchema({ alias, context, node });
|
|
1554
|
+
}
|
|
1555
|
+
function getDescribedColumnRefFromSchema({
|
|
1556
|
+
alias,
|
|
1557
|
+
context,
|
|
1558
|
+
node
|
|
1559
|
+
}) {
|
|
1560
|
+
if (isColumnStarRef(node.fields)) {
|
|
1561
|
+
return getDescribedColumnByResolvedColumns({
|
|
1562
|
+
alias,
|
|
1563
|
+
context,
|
|
1564
|
+
resolved: context.resolver.getAllResolvedColumns().map((x) => x.column)
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
if (isColumnTableStarRef(node.fields)) {
|
|
1568
|
+
return getDescribedColumnByResolvedColumns({
|
|
1569
|
+
alias,
|
|
1570
|
+
context,
|
|
1571
|
+
resolved: context.resolver.getResolvedColumnsInTable(node.fields[0].String.sval)
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
if (isColumnUnknownRef(node.fields)) {
|
|
1575
|
+
const resolved = context.resolver.getColumnsByTargetField({
|
|
1576
|
+
kind: "unknown",
|
|
1577
|
+
field: node.fields[0].String.sval
|
|
1578
|
+
});
|
|
1579
|
+
return getDescribedColumnByResolvedColumns({
|
|
1580
|
+
alias,
|
|
1581
|
+
context,
|
|
1582
|
+
resolved: resolved ?? []
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
if (isColumnTableColumnRef(node.fields)) {
|
|
1586
|
+
const resolved = context.resolver.getNestedResolvedTargetField({
|
|
1587
|
+
kind: "column",
|
|
1588
|
+
table: node.fields[0].String.sval,
|
|
1589
|
+
column: node.fields[1].String.sval
|
|
1590
|
+
});
|
|
1591
|
+
return getDescribedColumnByResolvedColumns({
|
|
1592
|
+
alias,
|
|
1593
|
+
context,
|
|
1594
|
+
resolved: checkSql_utils.fmap(resolved, (x) => [x]) ?? []
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
return [];
|
|
1598
|
+
}
|
|
1599
|
+
function getDescribedColumnByResolvedColumns(params) {
|
|
1600
|
+
return params.resolved.map(({ column, isNotNull }) => {
|
|
1601
|
+
const getType = () => {
|
|
1602
|
+
const overridenType = params.context.overridenColumnTypesMap.get(column.tableName)?.get(column.colName);
|
|
1603
|
+
if (overridenType !== void 0) {
|
|
1604
|
+
return {
|
|
1605
|
+
kind: "type",
|
|
1606
|
+
value: overridenType,
|
|
1607
|
+
type: params.context.pgTypes.get(column.colTypeOid)?.name ?? "unknown"
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
return params.context.toTypeScriptType({
|
|
1611
|
+
oid: column.colTypeOid,
|
|
1612
|
+
baseOid: column.colBaseTypeOid
|
|
1613
|
+
});
|
|
1614
|
+
};
|
|
1615
|
+
return {
|
|
1616
|
+
name: params.alias ?? column.colName,
|
|
1617
|
+
type: resolveType({
|
|
1618
|
+
context: params.context,
|
|
1619
|
+
nullable: !isNotNull,
|
|
1620
|
+
type: getType()
|
|
1621
|
+
})
|
|
1622
|
+
};
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
function getDescribedAConst({
|
|
1626
|
+
alias,
|
|
1627
|
+
context,
|
|
1628
|
+
node
|
|
1629
|
+
}) {
|
|
1630
|
+
const type = (() => {
|
|
1631
|
+
switch (true) {
|
|
1632
|
+
case node.boolval !== void 0:
|
|
1633
|
+
return {
|
|
1634
|
+
kind: "literal",
|
|
1635
|
+
value: node.boolval.boolval ? "true" : "false",
|
|
1636
|
+
base: context.toTypeScriptType({ name: "bool" })
|
|
1637
|
+
};
|
|
1638
|
+
case node.bsval !== void 0:
|
|
1639
|
+
return context.toTypeScriptType({ name: "bytea" });
|
|
1640
|
+
case node.fval !== void 0:
|
|
1641
|
+
return {
|
|
1642
|
+
kind: "literal",
|
|
1643
|
+
value: node.fval.toString(),
|
|
1644
|
+
base: context.toTypeScriptType({ name: "float8" })
|
|
1645
|
+
};
|
|
1646
|
+
case node.isnull !== void 0:
|
|
1647
|
+
return context.toTypeScriptType({ name: "null" });
|
|
1648
|
+
case node.ival !== void 0:
|
|
1649
|
+
return {
|
|
1650
|
+
kind: "literal",
|
|
1651
|
+
value: (node.ival.ival ?? 0).toString(),
|
|
1652
|
+
base: context.toTypeScriptType({ name: "int4" })
|
|
1653
|
+
};
|
|
1654
|
+
case node.sval !== void 0:
|
|
1655
|
+
return {
|
|
1656
|
+
kind: "literal",
|
|
1657
|
+
value: `'${node.sval.sval}'`,
|
|
1658
|
+
base: context.toTypeScriptType({ name: "text" })
|
|
1659
|
+
};
|
|
1660
|
+
default:
|
|
1661
|
+
return context.toTypeScriptType({ name: "unknown" });
|
|
1662
|
+
}
|
|
1663
|
+
})();
|
|
1664
|
+
return [
|
|
1665
|
+
{
|
|
1666
|
+
name: alias ?? "?column?",
|
|
1667
|
+
type: resolveType({ context, nullable: node.isnull === true, type })
|
|
1668
|
+
}
|
|
1669
|
+
];
|
|
1670
|
+
}
|
|
1671
|
+
function asNonNullableType(type) {
|
|
1672
|
+
switch (type.kind) {
|
|
1673
|
+
case "object":
|
|
1674
|
+
case "array":
|
|
1675
|
+
case "literal":
|
|
1676
|
+
return type;
|
|
1677
|
+
case "union": {
|
|
1678
|
+
const filtered = type.value.filter(
|
|
1679
|
+
(described) => described.kind !== "type" || described.value !== "null"
|
|
1680
|
+
);
|
|
1681
|
+
if (filtered.length === 0) {
|
|
1682
|
+
return { kind: "type", value: "unknown", type: "unknown" };
|
|
1683
|
+
}
|
|
1684
|
+
if (filtered.length === 1) {
|
|
1685
|
+
return filtered[0];
|
|
1686
|
+
}
|
|
1687
|
+
return { kind: "union", value: filtered };
|
|
1688
|
+
}
|
|
1689
|
+
case "type":
|
|
1690
|
+
return type.value === "null" ? { kind: "type", value: "unknown", type: "unknown" } : type;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
function isDescribedColumnNullable(type) {
|
|
1694
|
+
return type.kind === "union" && type.value.some((x) => x.kind === "type" && x.value === "null");
|
|
1695
|
+
}
|
|
1696
|
+
function resolveType(params) {
|
|
1697
|
+
if (params.nullable && params.type.value !== "null") {
|
|
1698
|
+
return {
|
|
1699
|
+
kind: "union",
|
|
1700
|
+
value: [params.type, params.context.toTypeScriptType({ name: "null" })]
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
return params.type;
|
|
1704
|
+
}
|
|
1705
|
+
function concatStringNodes(nodes) {
|
|
1706
|
+
return nodes?.map((x) => x.String?.sval).filter(Boolean).join(".") ?? "";
|
|
1707
|
+
}
|
|
1708
|
+
function hasColumnReference(node) {
|
|
1709
|
+
if (node === void 0) {
|
|
1710
|
+
return false;
|
|
1711
|
+
}
|
|
1712
|
+
if (node.ColumnRef !== void 0) {
|
|
1713
|
+
return true;
|
|
1714
|
+
}
|
|
1715
|
+
if (node.A_Const !== void 0) {
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
if (node.TypeCast !== void 0) {
|
|
1719
|
+
return hasColumnReference(node.TypeCast.arg);
|
|
1720
|
+
}
|
|
1721
|
+
if (node.A_Expr !== void 0) {
|
|
1722
|
+
return hasColumnReference(node.A_Expr.lexpr) || hasColumnReference(node.A_Expr.rexpr);
|
|
1723
|
+
}
|
|
1724
|
+
if (node.BoolExpr !== void 0) {
|
|
1725
|
+
return (node.BoolExpr.args ?? []).some(hasColumnReference);
|
|
1726
|
+
}
|
|
1727
|
+
if (node.FuncCall !== void 0) {
|
|
1728
|
+
return (node.FuncCall.args ?? []).some(hasColumnReference);
|
|
1729
|
+
}
|
|
1730
|
+
if (node.CoalesceExpr !== void 0) {
|
|
1731
|
+
return node.CoalesceExpr.args.some(hasColumnReference);
|
|
1732
|
+
}
|
|
1733
|
+
if (node.CaseExpr !== void 0) {
|
|
1734
|
+
if (node.CaseExpr.arg && hasColumnReference(node.CaseExpr.arg)) {
|
|
1735
|
+
return true;
|
|
1736
|
+
}
|
|
1737
|
+
if (node.CaseExpr.defresult && hasColumnReference(node.CaseExpr.defresult)) {
|
|
1738
|
+
return true;
|
|
1739
|
+
}
|
|
1740
|
+
return node.CaseExpr.args.some((caseWhen) => {
|
|
1741
|
+
if (caseWhen.CaseWhen === void 0) {
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
return hasColumnReference(caseWhen.CaseWhen.expr) || hasColumnReference(caseWhen.CaseWhen.result);
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
if (node.A_ArrayExpr !== void 0) {
|
|
1748
|
+
return (node.A_ArrayExpr.elements ?? []).some(hasColumnReference);
|
|
1749
|
+
}
|
|
1750
|
+
if (node.SubLink !== void 0) {
|
|
1751
|
+
return true;
|
|
1752
|
+
}
|
|
1753
|
+
return false;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
function isParsedInsertResult(parsed) {
|
|
1757
|
+
return parsed.stmts[0]?.stmt?.InsertStmt !== void 0;
|
|
1758
|
+
}
|
|
1759
|
+
function validateInsertResult(parsed, pgColsBySchemaAndTableName, query) {
|
|
1760
|
+
const insertStmt = parsed.stmts[0].stmt.InsertStmt;
|
|
1761
|
+
if (insertStmt.relation === void 0) {
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
const schemaName = insertStmt.relation.schemaname ?? "public";
|
|
1765
|
+
const tableName = insertStmt.relation.relname;
|
|
1766
|
+
const tableCols = pgColsBySchemaAndTableName.get(schemaName)?.get(tableName);
|
|
1767
|
+
if (tableCols === void 0) {
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
const insertCols = getInsertColumns(insertStmt, tableCols);
|
|
1771
|
+
const missing = tableCols.filter(
|
|
1772
|
+
(c) => c.colNotNull && !c.colHasDef && c.colIdentity === "" && !insertCols.includes(c.colName)
|
|
1773
|
+
);
|
|
1774
|
+
if (missing.length === 0) {
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
const position = (parsed.stmts[0]?.stmtLocation ?? 0) + 1;
|
|
1778
|
+
const columnsStr = missing.map((c) => `"${c.colName}"`).join(", ");
|
|
1779
|
+
const message = missing.length === 1 ? [
|
|
1780
|
+
`null value in column ${columnsStr} violates not-null constraint`,
|
|
1781
|
+
`Hint: Column ${columnsStr} is not nullable and has no default value.`
|
|
1782
|
+
].join("\n") : [
|
|
1783
|
+
`null value in columns ${columnsStr} violates not-null constraint`,
|
|
1784
|
+
`Hint: Columns ${columnsStr} are not nullable and have no default value.`
|
|
1785
|
+
].join("\n");
|
|
1786
|
+
throw checkSql_utils.PostgresError.of({
|
|
1787
|
+
queryText: query.text,
|
|
1788
|
+
message,
|
|
1789
|
+
line: "1",
|
|
1790
|
+
position,
|
|
1791
|
+
sourcemaps: query.sourcemaps
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
function getInsertColumns(stmt, tableCols) {
|
|
1795
|
+
if (stmt.cols) {
|
|
1796
|
+
return stmt.cols.map((col) => col.ResTarget?.name).filter((name) => Boolean(name));
|
|
1797
|
+
}
|
|
1798
|
+
if (stmt.selectStmt === void 0) {
|
|
1799
|
+
return [];
|
|
1800
|
+
}
|
|
1801
|
+
const valuesFromSelect = stmt.selectStmt.SelectStmt?.valuesLists?.at(0)?.List?.items;
|
|
1802
|
+
if (valuesFromSelect !== void 0) {
|
|
1803
|
+
return tableCols.slice(0, valuesFromSelect.length).map((c) => c.colName);
|
|
1804
|
+
}
|
|
1805
|
+
return tableCols.map((c) => c.colName);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
function createEmptyCache() {
|
|
1809
|
+
return {
|
|
1810
|
+
base: /* @__PURE__ */ new Map(),
|
|
1811
|
+
overrides: {
|
|
1812
|
+
types: /* @__PURE__ */ new Map(),
|
|
1813
|
+
columns: /* @__PURE__ */ new Map()
|
|
1814
|
+
},
|
|
1815
|
+
functions: /* @__PURE__ */ new Map()
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
function createGenerator() {
|
|
1819
|
+
const cache = createEmptyCache();
|
|
1820
|
+
return {
|
|
1821
|
+
generate: (params) => generate(params, cache),
|
|
1822
|
+
dropCacheKey: (cacheKey) => cache.base.delete(cacheKey),
|
|
1823
|
+
clearCache: () => {
|
|
1824
|
+
cache.base.clear();
|
|
1825
|
+
cache.overrides.types.clear();
|
|
1826
|
+
cache.overrides.columns.clear();
|
|
1827
|
+
cache.functions.clear();
|
|
1828
|
+
}
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
async function generate(params, cache) {
|
|
1832
|
+
const { sql, query, cacheKey, cacheMetadata = true } = params;
|
|
1833
|
+
const {
|
|
1834
|
+
pgColsByTableOidCache,
|
|
1835
|
+
pgColsBySchemaAndTableName,
|
|
1836
|
+
pgTypes,
|
|
1837
|
+
pgEnums,
|
|
1838
|
+
pgFnsByName,
|
|
1839
|
+
pgTypeExprMap
|
|
1840
|
+
} = await checkSql_utils.getOrSetFromMapWithEnabled({
|
|
1841
|
+
shouldCache: cacheMetadata,
|
|
1842
|
+
map: cache.base,
|
|
1843
|
+
key: cacheKey,
|
|
1844
|
+
value: () => getDatabaseMetadata(sql)
|
|
1845
|
+
});
|
|
1846
|
+
const typesMap = await checkSql_utils.getOrSetFromMapWithEnabled({
|
|
1847
|
+
shouldCache: cacheMetadata,
|
|
1848
|
+
map: cache.overrides.types,
|
|
1849
|
+
key: JSON.stringify(params.overrides?.types),
|
|
1850
|
+
value: () => {
|
|
1851
|
+
const map = /* @__PURE__ */ new Map([["array", { override: false, value: "array" }]]);
|
|
1852
|
+
for (const [key, value] of checkSql_utils.defaultTypesMap.entries()) {
|
|
1853
|
+
map.set(key, { override: false, value });
|
|
1854
|
+
}
|
|
1855
|
+
for (const [k, v] of Object.entries(params.overrides?.types ?? {})) {
|
|
1856
|
+
map.set(k, { override: true, value: typeof v === "string" ? v : v.return });
|
|
1857
|
+
}
|
|
1858
|
+
return map;
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
const overridenColumnTypesMap = await checkSql_utils.getOrSetFromMapWithEnabled({
|
|
1862
|
+
shouldCache: cacheMetadata,
|
|
1863
|
+
map: cache.overrides.columns,
|
|
1864
|
+
key: JSON.stringify(params.overrides?.columns),
|
|
1865
|
+
value: () => {
|
|
1866
|
+
const map = /* @__PURE__ */ new Map();
|
|
1867
|
+
for (const [colPath, type] of Object.entries(params.overrides?.columns ?? {})) {
|
|
1868
|
+
const [table, column] = colPath.split(".");
|
|
1869
|
+
if (table === void 0 || column === void 0) {
|
|
1870
|
+
throw new Error(`Invalid override column key: ${colPath}. Expected format: table.column`);
|
|
1871
|
+
}
|
|
1872
|
+
map.has(table) ? map.get(table)?.set(column, type) : map.set(table, /* @__PURE__ */ new Map([[column, type]]));
|
|
1873
|
+
}
|
|
1874
|
+
return map;
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
function byReturnType(a, b) {
|
|
1878
|
+
const priority = ["numeric", "int8"];
|
|
1879
|
+
return priority.indexOf(a.returnType) - priority.indexOf(b.returnType);
|
|
1880
|
+
}
|
|
1881
|
+
const functionsMap = await checkSql_utils.getOrSetFromMapWithEnabled({
|
|
1882
|
+
shouldCache: cacheMetadata,
|
|
1883
|
+
map: cache.functions,
|
|
1884
|
+
key: JSON.stringify(params.overrides?.types),
|
|
1885
|
+
value: () => {
|
|
1886
|
+
const map = /* @__PURE__ */ new Map();
|
|
1887
|
+
for (const [functionName, signatures] of pgFnsByName.entries()) {
|
|
1888
|
+
for (const signature of signatures.sort(byReturnType)) {
|
|
1889
|
+
const tsArgs = signature.arguments.map((arg) => {
|
|
1890
|
+
return typesMap.get(arg)?.value ?? "unknown";
|
|
1891
|
+
});
|
|
1892
|
+
const tsReturnType = typesMap.get(signature.returnType)?.value ?? signature.returnType;
|
|
1893
|
+
const key = tsArgs.length === 0 ? functionName : `${functionName}(${tsArgs.join(", ")})`;
|
|
1894
|
+
map.set(key, { ts: tsReturnType, pg: signature.returnType });
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
return map;
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1900
|
+
try {
|
|
1901
|
+
const result = await sql.unsafe(query.text, [], { prepare: true }).describe();
|
|
1902
|
+
const parsed = await parser__namespace.parse(query.text);
|
|
1903
|
+
if (isParsedInsertResult(parsed)) {
|
|
1904
|
+
validateInsertResult(parsed, pgColsBySchemaAndTableName, query);
|
|
1905
|
+
}
|
|
1906
|
+
if (result.columns === void 0 || result.columns === null || result.columns.length === 0) {
|
|
1907
|
+
return fpTs.either.right({ output: null, unknownColumns: [], stmt: result, query });
|
|
1908
|
+
}
|
|
1909
|
+
const duplicateCols = result.columns.filter(
|
|
1910
|
+
(col, index) => result.columns.find((c, i) => c.name === col.name && i != index)
|
|
1911
|
+
);
|
|
1912
|
+
if (duplicateCols.length > 0) {
|
|
1913
|
+
const dupes = duplicateCols.map((col) => {
|
|
1914
|
+
const tableCols = pgColsByTableOidCache.get(col.table);
|
|
1915
|
+
const tableCol = col.number !== 0 ? tableCols?.at(col.number - 1) : tableCols?.find((c) => c.colName === col.name);
|
|
1916
|
+
return {
|
|
1917
|
+
table: tableCol?.tableName,
|
|
1918
|
+
column: col.name,
|
|
1919
|
+
originalColumn: tableCol?.colName
|
|
1920
|
+
};
|
|
1921
|
+
});
|
|
1922
|
+
const dupePosition = (() => {
|
|
1923
|
+
const match = query.text.search(new RegExp(`\\b${duplicateCols[0].name}\\b`));
|
|
1924
|
+
return (match > -1 ? match : query.text.search(/SELECT/i)) + 1;
|
|
1925
|
+
})();
|
|
1926
|
+
return fpTs.either.left(
|
|
1927
|
+
checkSql_utils.DuplicateColumnsError.of({
|
|
1928
|
+
queryText: query.text,
|
|
1929
|
+
sourcemaps: query.sourcemaps,
|
|
1930
|
+
position: dupePosition,
|
|
1931
|
+
columns: dupes.map((x) => {
|
|
1932
|
+
return x.column !== x.originalColumn ? `${x.table}.${x.originalColumn} (alias: ${x.column})` : `${x.table}.${x.column}`;
|
|
1933
|
+
})
|
|
1934
|
+
})
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
const astQueryDescription = getASTDescription({
|
|
1938
|
+
parsed,
|
|
1939
|
+
typesMap,
|
|
1940
|
+
overridenColumnTypesMap,
|
|
1941
|
+
pgColsBySchemaAndTableName,
|
|
1942
|
+
pgTypes,
|
|
1943
|
+
pgEnums,
|
|
1944
|
+
pgFns: functionsMap,
|
|
1945
|
+
typeExprMap: pgTypeExprMap,
|
|
1946
|
+
fieldTransform: params.fieldTransform
|
|
1947
|
+
});
|
|
1948
|
+
const columns = result.columns.map((col, position) => {
|
|
1949
|
+
const introspected = pgColsByTableOidCache.get(col.table)?.find((x) => x.colNum === col.number);
|
|
1950
|
+
const astDescribed = astQueryDescription.map.get(position);
|
|
1951
|
+
return {
|
|
1952
|
+
described: col,
|
|
1953
|
+
astDescribed,
|
|
1954
|
+
introspected,
|
|
1955
|
+
isNonNullableBasedOnAST: astQueryDescription.meta.nonNullableColumns.has(col.name)
|
|
1956
|
+
};
|
|
1957
|
+
});
|
|
1958
|
+
const context = {
|
|
1959
|
+
columns,
|
|
1960
|
+
pgTypes,
|
|
1961
|
+
pgEnums,
|
|
1962
|
+
relationsWithJoins: astQueryDescription.meta.relations,
|
|
1963
|
+
overrides: {
|
|
1964
|
+
types: typesMap,
|
|
1965
|
+
columns: overridenColumnTypesMap
|
|
1966
|
+
},
|
|
1967
|
+
pgColsBySchemaAndTableName,
|
|
1968
|
+
fieldTransform: params.fieldTransform
|
|
1969
|
+
};
|
|
1970
|
+
return fpTs.either.right({
|
|
1971
|
+
output: getTypedColumnEntries({ context }),
|
|
1972
|
+
unknownColumns: columns.filter((x) => x.astDescribed === void 0).map((x) => x.described.name),
|
|
1973
|
+
stmt: result,
|
|
1974
|
+
query
|
|
1975
|
+
});
|
|
1976
|
+
} catch (e) {
|
|
1977
|
+
if (checkSql_utils.isPostgresError(e)) {
|
|
1978
|
+
return fpTs.either.left(
|
|
1979
|
+
checkSql_utils.PostgresError.of({
|
|
1980
|
+
queryText: query.text,
|
|
1981
|
+
sourcemaps: query.sourcemaps,
|
|
1982
|
+
message: e.message,
|
|
1983
|
+
line: e.line,
|
|
1984
|
+
position: e.position
|
|
1985
|
+
})
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
throw e;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
async function getDatabaseMetadata(sql) {
|
|
1992
|
+
const pgTypes = await getPgTypes(sql);
|
|
1993
|
+
const pgCols = await getPgCols(sql);
|
|
1994
|
+
const pgEnums = await getPgEnums(sql);
|
|
1995
|
+
const pgFns = await getPgFunctions(sql);
|
|
1996
|
+
const pgColsByTableOidCache = checkSql_utils.groupBy(pgCols, "tableOid");
|
|
1997
|
+
const pgColsBySchemaAndTableName = checkSql_utils.groupBy(pgCols, "schemaName", "tableName");
|
|
1998
|
+
const pgFnsByName = checkSql_utils.groupBy(pgFns, "name");
|
|
1999
|
+
const pgTypeExprMap = await getPgTypeExprMap(sql);
|
|
2000
|
+
return {
|
|
2001
|
+
pgTypes,
|
|
2002
|
+
pgCols,
|
|
2003
|
+
pgEnums,
|
|
2004
|
+
pgColsByTableOidCache,
|
|
2005
|
+
pgColsBySchemaAndTableName,
|
|
2006
|
+
pgFnsByName,
|
|
2007
|
+
pgTypeExprMap
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
function getTypedColumnEntries(params) {
|
|
2011
|
+
const value = params.context.columns.map(
|
|
2012
|
+
(col) => getResolvedTargetEntry({ col, context: params.context })
|
|
2013
|
+
);
|
|
2014
|
+
return { kind: "object", value };
|
|
2015
|
+
}
|
|
2016
|
+
function isNullableResolvedTarget(target) {
|
|
2017
|
+
switch (target.kind) {
|
|
2018
|
+
case "type":
|
|
2019
|
+
return ["any", "null"].includes(target.value) === false;
|
|
2020
|
+
case "union":
|
|
2021
|
+
return target.value.some(isNullableResolvedTarget);
|
|
2022
|
+
case "array":
|
|
2023
|
+
return isNullableResolvedTarget(target.value);
|
|
2024
|
+
case "object":
|
|
2025
|
+
return target.value.some(([, value]) => isNullableResolvedTarget(value));
|
|
2026
|
+
case "literal":
|
|
2027
|
+
return false;
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
function buildInterfacePropertyValue(params) {
|
|
2031
|
+
const isNullable = params.isNullable && isNullableResolvedTarget(params.value);
|
|
2032
|
+
return [
|
|
2033
|
+
params.key,
|
|
2034
|
+
isNullable ? { kind: "union", value: [params.value, { kind: "type", value: "null", type: "null" }] } : params.value
|
|
2035
|
+
];
|
|
2036
|
+
}
|
|
2037
|
+
function checkIsNullableDueToRelation(params) {
|
|
2038
|
+
const { col, relationsWithJoins } = params;
|
|
2039
|
+
const findByJoin = relationsWithJoins.find((x) => x.joinRelName === col.tableName);
|
|
2040
|
+
if (findByJoin !== void 0) {
|
|
2041
|
+
switch (findByJoin.joinType) {
|
|
2042
|
+
case JoinType.JOIN_LEFT:
|
|
2043
|
+
case JoinType.JOIN_FULL:
|
|
2044
|
+
return true;
|
|
2045
|
+
case JoinType.JOIN_TYPE_UNDEFINED:
|
|
2046
|
+
case JoinType.JOIN_INNER:
|
|
2047
|
+
case JoinType.JOIN_RIGHT:
|
|
2048
|
+
case JoinType.JOIN_SEMI:
|
|
2049
|
+
case JoinType.JOIN_ANTI:
|
|
2050
|
+
case JoinType.JOIN_UNIQUE_OUTER:
|
|
2051
|
+
case JoinType.JOIN_UNIQUE_INNER:
|
|
2052
|
+
case JoinType.UNRECOGNIZED:
|
|
2053
|
+
return false;
|
|
2054
|
+
default:
|
|
2055
|
+
checkSql_utils.assertNever(findByJoin.joinType);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
const findByRel = relationsWithJoins.filter((x) => x.relName === col.tableName);
|
|
2059
|
+
for (const rel of findByRel) {
|
|
2060
|
+
switch (rel.joinType) {
|
|
2061
|
+
case JoinType.JOIN_RIGHT:
|
|
2062
|
+
case JoinType.JOIN_FULL:
|
|
2063
|
+
return true;
|
|
2064
|
+
case JoinType.JOIN_TYPE_UNDEFINED:
|
|
2065
|
+
case JoinType.JOIN_INNER:
|
|
2066
|
+
case JoinType.JOIN_LEFT:
|
|
2067
|
+
case JoinType.JOIN_SEMI:
|
|
2068
|
+
case JoinType.JOIN_ANTI:
|
|
2069
|
+
case JoinType.JOIN_UNIQUE_OUTER:
|
|
2070
|
+
case JoinType.JOIN_UNIQUE_INNER:
|
|
2071
|
+
case JoinType.UNRECOGNIZED:
|
|
2072
|
+
return false;
|
|
2073
|
+
default:
|
|
2074
|
+
checkSql_utils.assertNever(rel.joinType);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
return false;
|
|
2078
|
+
}
|
|
2079
|
+
function getResolvedTargetEntry(params) {
|
|
2080
|
+
if (params.col.astDescribed !== void 0) {
|
|
2081
|
+
return [
|
|
2082
|
+
checkSql_utils.toCase(params.col.described.name, params.context.fieldTransform),
|
|
2083
|
+
params.col.astDescribed.type
|
|
2084
|
+
];
|
|
2085
|
+
}
|
|
2086
|
+
const pgTypeOid = params.col.introspected?.colBaseTypeOid ?? params.col.described.type;
|
|
2087
|
+
const pgType = params.context.pgTypes.get(pgTypeOid);
|
|
2088
|
+
const getEnumByOid = (oid) => {
|
|
2089
|
+
const pgEnum = params.context.pgEnums.get(oid);
|
|
2090
|
+
if (pgEnum === void 0) {
|
|
2091
|
+
return void 0;
|
|
2092
|
+
}
|
|
2093
|
+
return {
|
|
2094
|
+
kind: "union",
|
|
2095
|
+
value: pgEnum.values.map(
|
|
2096
|
+
(value2) => ({
|
|
2097
|
+
kind: "type",
|
|
2098
|
+
value: `'${value2}'`,
|
|
2099
|
+
type: pgEnum.name
|
|
2100
|
+
})
|
|
2101
|
+
)
|
|
2102
|
+
};
|
|
2103
|
+
};
|
|
2104
|
+
const valueAsEnum = (() => {
|
|
2105
|
+
const enumTarget = getEnumByOid(pgTypeOid);
|
|
2106
|
+
if (enumTarget !== void 0) {
|
|
2107
|
+
return enumTarget;
|
|
2108
|
+
}
|
|
2109
|
+
if (pgType?.typelem !== void 0 && pgType.typelem !== 0) {
|
|
2110
|
+
const elemEnumTarget = getEnumByOid(pgType.typelem);
|
|
2111
|
+
if (elemEnumTarget !== void 0) {
|
|
2112
|
+
return { kind: "array", value: elemEnumTarget };
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return void 0;
|
|
2116
|
+
})();
|
|
2117
|
+
const valueAsType = getTsTypeFromPgTypeOid({
|
|
2118
|
+
pgTypeOid,
|
|
2119
|
+
pgTypes: params.context.pgTypes
|
|
2120
|
+
});
|
|
2121
|
+
const valueAsOverride = (() => {
|
|
2122
|
+
const pgType2 = params.context.pgTypes.get(
|
|
2123
|
+
params.col.introspected?.colTypeOid ?? params.col.described.type
|
|
2124
|
+
);
|
|
2125
|
+
if (!pgType2) return void 0;
|
|
2126
|
+
const columnOverride = params?.context?.overrides?.columns.get(params.col.introspected?.tableName ?? "")?.get(params.col.introspected?.colName ?? params.col.described.name);
|
|
2127
|
+
if (columnOverride !== void 0) {
|
|
2128
|
+
return { kind: "type", value: columnOverride, type: pgType2.name };
|
|
2129
|
+
}
|
|
2130
|
+
const typeOverride = params?.context?.overrides?.types.get(pgType2.name);
|
|
2131
|
+
if (typeOverride !== void 0) {
|
|
2132
|
+
return {
|
|
2133
|
+
kind: "type",
|
|
2134
|
+
value: typeOverride.value,
|
|
2135
|
+
type: pgType2.name
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
return void 0;
|
|
2139
|
+
})();
|
|
2140
|
+
const value = valueAsOverride ?? valueAsEnum ?? valueAsType;
|
|
2141
|
+
const key = params.col.described.name ?? params.col.introspected?.colName;
|
|
2142
|
+
let isNonNullable = params.col.isNonNullableBasedOnAST;
|
|
2143
|
+
if (!isNonNullable && params.col.introspected !== void 0) {
|
|
2144
|
+
isNonNullable = params.col.introspected.colNotNull;
|
|
2145
|
+
if (checkIsNullableDueToRelation({
|
|
2146
|
+
col: params.col.introspected,
|
|
2147
|
+
relationsWithJoins: params.context.relationsWithJoins
|
|
2148
|
+
})) {
|
|
2149
|
+
isNonNullable = false;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
return buildInterfacePropertyValue({
|
|
2153
|
+
key: checkSql_utils.toCase(key, params.context.fieldTransform),
|
|
2154
|
+
value,
|
|
2155
|
+
isNullable: !isNonNullable
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
function getTsTypeFromPgTypeOid(params) {
|
|
2159
|
+
const pgType = params.pgTypes.get(params.pgTypeOid);
|
|
2160
|
+
if (pgType === void 0) {
|
|
2161
|
+
return { kind: "type", value: "unknown", type: "unknown" };
|
|
2162
|
+
}
|
|
2163
|
+
return getTsTypeFromPgType({ pgTypeName: pgType.name });
|
|
2164
|
+
}
|
|
2165
|
+
function getTsTypeFromPgType(params) {
|
|
2166
|
+
const { isArray, pgType } = parsePgType(params.pgTypeName);
|
|
2167
|
+
const tsType = checkSql_utils.defaultTypesMap.get(pgType) ?? "any";
|
|
2168
|
+
const property = { kind: "type", value: tsType, type: pgType };
|
|
2169
|
+
return isArray ? { kind: "array", value: property } : property;
|
|
2170
|
+
}
|
|
2171
|
+
function isPgTypeArray(pgType) {
|
|
2172
|
+
return pgType.startsWith("_");
|
|
2173
|
+
}
|
|
2174
|
+
function parsePgType(pgType) {
|
|
2175
|
+
const isArray = isPgTypeArray(pgType);
|
|
2176
|
+
return {
|
|
2177
|
+
isArray,
|
|
2178
|
+
pgType: isArray ? pgType.slice(1) : pgType
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
async function getPgEnums(sql) {
|
|
2182
|
+
const rows = await sql`
|
|
2183
|
+
SELECT pg_type.oid, pg_type.typname, pg_enum.enumlabel
|
|
2184
|
+
FROM pg_type
|
|
2185
|
+
JOIN pg_enum ON pg_enum.enumtypid = pg_type.oid
|
|
2186
|
+
WHERE pg_type.typtype = 'e'
|
|
2187
|
+
ORDER BY pg_type.typname, pg_enum.enumsortorder
|
|
2188
|
+
`;
|
|
2189
|
+
const map = /* @__PURE__ */ new Map();
|
|
2190
|
+
for (const row of rows) {
|
|
2191
|
+
const existing = map.get(row.oid);
|
|
2192
|
+
if (existing === void 0) {
|
|
2193
|
+
map.set(row.oid, {
|
|
2194
|
+
name: row.typname,
|
|
2195
|
+
values: [row.enumlabel]
|
|
2196
|
+
});
|
|
2197
|
+
continue;
|
|
2198
|
+
}
|
|
2199
|
+
existing.values.push(row.enumlabel);
|
|
2200
|
+
}
|
|
2201
|
+
return map;
|
|
2202
|
+
}
|
|
2203
|
+
async function getPgTypes(sql) {
|
|
2204
|
+
const rows = await sql`
|
|
2205
|
+
SELECT oid, typname as name, typelem FROM pg_type
|
|
2206
|
+
`;
|
|
2207
|
+
const map = /* @__PURE__ */ new Map();
|
|
2208
|
+
for (const row of rows) {
|
|
2209
|
+
map.set(row.oid, row);
|
|
2210
|
+
}
|
|
2211
|
+
return map;
|
|
2212
|
+
}
|
|
2213
|
+
async function getPgCols(sql) {
|
|
2214
|
+
const rows = await sql`
|
|
2215
|
+
SELECT
|
|
2216
|
+
pg_class.oid AS "tableOid",
|
|
2217
|
+
pg_namespace.nspname AS "schemaName",
|
|
2218
|
+
pg_class.relname AS "tableName",
|
|
2219
|
+
pg_attribute.attname AS "colName",
|
|
2220
|
+
pg_type.oid AS "colTypeOid",
|
|
2221
|
+
CASE
|
|
2222
|
+
WHEN pg_type.typtype = 'd' THEN
|
|
2223
|
+
(SELECT pt.oid FROM pg_type pt WHERE pt.oid = pg_type.typbasetype)
|
|
2224
|
+
ELSE
|
|
2225
|
+
NULL
|
|
2226
|
+
END AS "colBaseTypeOid",
|
|
2227
|
+
pg_attribute.attnum AS "colNum",
|
|
2228
|
+
pg_attribute.atthasdef "colHasDef",
|
|
2229
|
+
pg_attribute.attnotnull "colNotNull",
|
|
2230
|
+
pg_attribute.attidentity "colIdentity"
|
|
2231
|
+
FROM
|
|
2232
|
+
pg_attribute,
|
|
2233
|
+
pg_class,
|
|
2234
|
+
pg_type,
|
|
2235
|
+
pg_namespace
|
|
2236
|
+
WHERE TRUE
|
|
2237
|
+
AND pg_attribute.attrelid = pg_class.oid
|
|
2238
|
+
AND pg_attribute.atttypid = pg_type.oid
|
|
2239
|
+
AND pg_class.relnamespace = pg_namespace.oid
|
|
2240
|
+
AND pg_attribute.attnum >= 1
|
|
2241
|
+
ORDER BY
|
|
2242
|
+
pg_class.relname,
|
|
2243
|
+
pg_attribute.attnum
|
|
2244
|
+
`;
|
|
2245
|
+
return rows;
|
|
2246
|
+
}
|
|
2247
|
+
async function getPgFunctions(sql) {
|
|
2248
|
+
const rows = await sql`
|
|
2249
|
+
SELECT
|
|
2250
|
+
pg_proc.proname AS "name",
|
|
2251
|
+
pg_catalog.pg_get_function_arguments(pg_proc.oid) AS "argumentsString",
|
|
2252
|
+
pg_type.typname AS "returnType"
|
|
2253
|
+
FROM
|
|
2254
|
+
pg_catalog.pg_proc
|
|
2255
|
+
JOIN
|
|
2256
|
+
pg_catalog.pg_type ON pg_proc.prorettype = pg_type.oid
|
|
2257
|
+
WHERE
|
|
2258
|
+
pg_proc.pronamespace::regnamespace = 'pg_catalog'::regnamespace
|
|
2259
|
+
`;
|
|
2260
|
+
return rows.map((row) => ({
|
|
2261
|
+
name: row.name,
|
|
2262
|
+
arguments: row.argumentsString.replace(/"/, "").split(", ").filter((x) => x !== ""),
|
|
2263
|
+
returnType: row.returnType
|
|
2264
|
+
}));
|
|
2265
|
+
}
|
|
2266
|
+
async function getPgTypeExprMap(sql) {
|
|
2267
|
+
const rows = await sql`
|
|
2268
|
+
SELECT
|
|
2269
|
+
l.typname as left,
|
|
2270
|
+
o.oprname as operator,
|
|
2271
|
+
r.typname as right,
|
|
2272
|
+
ret.typname AS result
|
|
2273
|
+
FROM pg_operator o
|
|
2274
|
+
JOIN pg_type l ON l.oid = o.oprleft
|
|
2275
|
+
JOIN pg_type r ON r.oid = o.oprright
|
|
2276
|
+
JOIN pg_type ret ON ret.oid = o.oprresult
|
|
2277
|
+
WHERE o.oprleft <> 0 AND o.oprright <> 0
|
|
2278
|
+
`;
|
|
2279
|
+
const map = /* @__PURE__ */ new Map();
|
|
2280
|
+
for (const row of rows) {
|
|
2281
|
+
const leftMap = map.get(row.left) ?? /* @__PURE__ */ new Map();
|
|
2282
|
+
const rightMap = leftMap.get(row.operator) ?? /* @__PURE__ */ new Map();
|
|
2283
|
+
rightMap.set(row.right, row.result);
|
|
2284
|
+
leftMap.set(row.operator, rightMap);
|
|
2285
|
+
map.set(row.left, leftMap);
|
|
2286
|
+
}
|
|
2287
|
+
return map;
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
function createConnectionManager() {
|
|
2291
|
+
const connectionMap = /* @__PURE__ */ new Map();
|
|
2292
|
+
return {
|
|
2293
|
+
getOrCreate: (databaseUrl, options) => getOrCreateConnection(databaseUrl, connectionMap, options),
|
|
2294
|
+
close: (params) => closeConnection(params, connectionMap)
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
function getOrCreateConnection(databaseUrl, connectionMap, options) {
|
|
2298
|
+
return function_js.pipe(
|
|
2299
|
+
O__namespace.fromNullable(connectionMap.get(databaseUrl)),
|
|
2300
|
+
O__namespace.foldW(
|
|
2301
|
+
() => {
|
|
2302
|
+
const sql = postgres__default(databaseUrl, options);
|
|
2303
|
+
connectionMap.set(databaseUrl, sql);
|
|
2304
|
+
return { sql, databaseUrl, isFirst: true };
|
|
2305
|
+
},
|
|
2306
|
+
(sql) => ({ sql, databaseUrl, isFirst: false })
|
|
2307
|
+
)
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
function closeConnection(params, connectionMap) {
|
|
2311
|
+
const { connection, projectDir } = params;
|
|
2312
|
+
const strategy = checkSql_utils.getConnectionStrategyByRuleOptionConnection({ connection, projectDir });
|
|
2313
|
+
tsPattern.match(strategy).with({ type: "databaseUrl" }, ({ databaseUrl }) => {
|
|
2314
|
+
const sql = connectionMap.get(databaseUrl);
|
|
2315
|
+
if (sql) {
|
|
2316
|
+
sql.end();
|
|
2317
|
+
connectionMap.delete(databaseUrl);
|
|
2318
|
+
}
|
|
2319
|
+
}).with({ type: "migrations" }, ({ connectionUrl, databaseName }) => {
|
|
2320
|
+
const connectionOptions = { ...checkSql_utils.parseConnection(connectionUrl), database: databaseName };
|
|
2321
|
+
const databaseUrl = checkSql_utils.mapConnectionOptionsToString(connectionOptions);
|
|
2322
|
+
const sql = connectionMap.get(databaseUrl);
|
|
2323
|
+
const migrationSql = connectionMap.get(connectionUrl);
|
|
2324
|
+
if (sql) {
|
|
2325
|
+
sql.end();
|
|
2326
|
+
connectionMap.delete(databaseUrl);
|
|
2327
|
+
}
|
|
2328
|
+
if (migrationSql) {
|
|
2329
|
+
migrationSql.end();
|
|
2330
|
+
connectionMap.delete(connectionUrl);
|
|
2331
|
+
}
|
|
2332
|
+
}).exhaustive();
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
function createWatchManager() {
|
|
2336
|
+
const migrationPaths = /* @__PURE__ */ new Map();
|
|
2337
|
+
const watcher = chokidar__default.watch([], { ignoreInitial: true }).on("all", (_, filePath) => {
|
|
2338
|
+
for (const [path2, { onChange }] of migrationPaths.entries()) {
|
|
2339
|
+
if (filePath.startsWith(path2)) {
|
|
2340
|
+
return onChange();
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
});
|
|
2344
|
+
const watchMigrationsDir = (params) => {
|
|
2345
|
+
const migrationPath = path__default.join(params.projectDir, params.connection.migrationsDir);
|
|
2346
|
+
if (migrationPaths.has(migrationPath)) {
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
migrationPaths.set(migrationPath, {
|
|
2350
|
+
onChange: () => {
|
|
2351
|
+
const { databaseUrl } = checkSql_utils.getMigrationDatabaseMetadata({
|
|
2352
|
+
connectionUrl: params.connection.connectionUrl ?? checkSql_utils.DEFAULT_CONNECTION_URL,
|
|
2353
|
+
databaseName: checkSql_utils.getDatabaseName({
|
|
2354
|
+
databaseName: params.connection.databaseName,
|
|
2355
|
+
migrationsDir: params.connection.migrationsDir,
|
|
2356
|
+
projectDir: params.projectDir
|
|
2357
|
+
})
|
|
2358
|
+
});
|
|
2359
|
+
params.dropCacheKeyFn(databaseUrl);
|
|
2360
|
+
params.closeConnectionFn({ connection: params.connection, projectDir: params.projectDir });
|
|
2361
|
+
}
|
|
2362
|
+
});
|
|
2363
|
+
watcher.add(migrationPath);
|
|
2364
|
+
};
|
|
2365
|
+
return {
|
|
2366
|
+
watchMigrationsDir
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
const generator = createGenerator();
|
|
2371
|
+
const connections = createConnectionManager();
|
|
2372
|
+
const watchers = createWatchManager();
|
|
2373
|
+
async function handler(params) {
|
|
2374
|
+
if (checkSql_utils.isWatchMigrationsDirEnabled(params.connection)) {
|
|
2375
|
+
watchers.watchMigrationsDir({
|
|
2376
|
+
connection: params.connection,
|
|
2377
|
+
projectDir: params.projectDir,
|
|
2378
|
+
dropCacheKeyFn: generator.dropCacheKey,
|
|
2379
|
+
closeConnectionFn: connections.close
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
const result = await function_js.pipe(
|
|
2383
|
+
TE__namespace.Do,
|
|
2384
|
+
TE__namespace.chain(() => workerHandler(params))
|
|
2385
|
+
)();
|
|
2386
|
+
if (params.connection.keepAlive === false) {
|
|
2387
|
+
connections.close({ connection: params.connection, projectDir: params.projectDir });
|
|
2388
|
+
}
|
|
2389
|
+
return J__namespace.stringify(result);
|
|
2390
|
+
}
|
|
2391
|
+
synckit.runAsWorker(handler);
|
|
2392
|
+
function workerHandler(params) {
|
|
2393
|
+
const strategy = checkSql_utils.getConnectionStrategyByRuleOptionConnection(params);
|
|
2394
|
+
const connectionPayload = tsPattern.match(strategy).with(
|
|
2395
|
+
{ type: "databaseUrl" },
|
|
2396
|
+
({ databaseUrl }) => TE__namespace.right(connections.getOrCreate(databaseUrl))
|
|
2397
|
+
).with({ type: "migrations" }, ({ migrationsDir, databaseName, connectionUrl }) => {
|
|
2398
|
+
const { connectionOptions, databaseUrl } = checkSql_utils.getMigrationDatabaseMetadata({
|
|
2399
|
+
connectionUrl,
|
|
2400
|
+
databaseName
|
|
2401
|
+
});
|
|
2402
|
+
const { sql, isFirst } = connections.getOrCreate(databaseUrl);
|
|
2403
|
+
const { sql: migrationSql } = connections.getOrCreate(connectionUrl, {
|
|
2404
|
+
onnotice: () => {
|
|
2405
|
+
}
|
|
2406
|
+
});
|
|
2407
|
+
const connectionPayload2 = { sql, isFirst, databaseUrl };
|
|
2408
|
+
if (isFirst) {
|
|
2409
|
+
const migrationsPath = path__default.join(params.projectDir, migrationsDir);
|
|
2410
|
+
return function_js.pipe(
|
|
2411
|
+
TE__namespace.Do,
|
|
2412
|
+
TE__namespace.chainW(() => checkSql_utils.initDatabase(migrationSql, connectionOptions.database)),
|
|
2413
|
+
TE__namespace.chainW(() => checkSql_utils.runMigrations({ migrationsPath, sql })),
|
|
2414
|
+
TE__namespace.map(() => connectionPayload2)
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
return TE__namespace.right(connectionPayload2);
|
|
2418
|
+
}).exhaustive();
|
|
2419
|
+
const generateTask = (params2) => {
|
|
2420
|
+
return TE__namespace.tryCatch(() => generator.generate(params2), checkSql_utils.InternalError.to);
|
|
2421
|
+
};
|
|
2422
|
+
return function_js.pipe(
|
|
2423
|
+
connectionPayload,
|
|
2424
|
+
TE__namespace.chainW(({ sql, databaseUrl }) => {
|
|
2425
|
+
return generateTask({
|
|
2426
|
+
sql,
|
|
2427
|
+
query: params.query,
|
|
2428
|
+
cacheKey: databaseUrl,
|
|
2429
|
+
overrides: params.connection.overrides,
|
|
2430
|
+
fieldTransform: params.target.fieldTransform
|
|
2431
|
+
});
|
|
2432
|
+
}),
|
|
2433
|
+
TE__namespace.chainW(TE__namespace.fromEither)
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
//# sourceMappingURL=check-sql.worker.cjs.map
|