@onreza/sqlx-js 0.2.0 → 0.4.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/README.md +73 -29
- package/ROADMAP.md +2 -1
- package/dist/bin/sqlx-js.js +51 -11
- package/dist/src/cache.d.ts +1 -0
- package/dist/src/cache.js +128 -1
- package/dist/src/codegen.d.ts +2 -1
- package/dist/src/codegen.js +36 -17
- package/dist/src/commands/init.d.ts +1 -0
- package/dist/src/commands/init.js +36 -5
- package/dist/src/commands/pgschema.d.ts +25 -0
- package/dist/src/commands/pgschema.js +178 -0
- package/dist/src/commands/prepare.d.ts +1 -0
- package/dist/src/commands/prepare.js +48 -15
- package/dist/src/config.d.ts +6 -0
- package/dist/src/function-cache.d.ts +18 -0
- package/dist/src/function-cache.js +38 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/pg/functions.d.ts +4 -0
- package/dist/src/pg/functions.js +150 -0
- package/dist/src/pg/oids.js +5 -2
- package/dist/src/pg/param-map.d.ts +2 -1
- package/dist/src/pg/param-map.js +181 -40
- package/dist/src/pg/schema.d.ts +1 -0
- package/dist/src/pg/schema.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { decodeText } from "./wire.js";
|
|
2
|
+
const JSON_OIDS = new Set([114, 3802]);
|
|
3
|
+
const JSON_ARRAY_OIDS = new Set([199, 3807]);
|
|
4
|
+
const JSON_INPUT = 'import("@onreza/sqlx-js").JsonInput';
|
|
5
|
+
export async function introspectFunctions(client, schema) {
|
|
6
|
+
const rows = await loadFunctionRows(client);
|
|
7
|
+
const typeOids = new Set();
|
|
8
|
+
for (const row of rows) {
|
|
9
|
+
typeOids.add(row.returnOid);
|
|
10
|
+
for (const oid of row.inputArgOids)
|
|
11
|
+
typeOids.add(oid);
|
|
12
|
+
for (const oid of row.allArgOids ?? [])
|
|
13
|
+
typeOids.add(oid);
|
|
14
|
+
}
|
|
15
|
+
await schema.loadCustomTypes([...typeOids]);
|
|
16
|
+
return rows.map((row) => toEntry(row, schema)).sort((a, b) => a.signature.localeCompare(b.signature));
|
|
17
|
+
}
|
|
18
|
+
async function loadFunctionRows(client) {
|
|
19
|
+
const result = await client.simpleQueryAll(`
|
|
20
|
+
SELECT
|
|
21
|
+
n.nspname,
|
|
22
|
+
p.proname,
|
|
23
|
+
p.prokind::text,
|
|
24
|
+
pg_get_function_identity_arguments(p.oid),
|
|
25
|
+
to_json(
|
|
26
|
+
CASE
|
|
27
|
+
WHEN p.proargtypes::text = '' THEN ARRAY[]::oid[]
|
|
28
|
+
ELSE string_to_array(p.proargtypes::text, ' ')::oid[]
|
|
29
|
+
END
|
|
30
|
+
)::text,
|
|
31
|
+
to_json(p.proallargtypes)::text,
|
|
32
|
+
to_json(p.proargmodes)::text,
|
|
33
|
+
to_json(p.proargnames)::text,
|
|
34
|
+
p.prorettype::int8,
|
|
35
|
+
p.proretset
|
|
36
|
+
FROM pg_proc p
|
|
37
|
+
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
38
|
+
WHERE ${userSchemaFilter("n")}
|
|
39
|
+
ORDER BY n.nspname, p.proname, pg_get_function_identity_arguments(p.oid)
|
|
40
|
+
`);
|
|
41
|
+
return result.rows.map((row) => ({
|
|
42
|
+
schema: decodeText(row[0]),
|
|
43
|
+
name: decodeText(row[1]),
|
|
44
|
+
kind: functionKind(decodeText(row[2])),
|
|
45
|
+
identityArguments: decodeText(row[3]) ?? "",
|
|
46
|
+
inputArgOids: parseNumberJsonArray(decodeText(row[4])),
|
|
47
|
+
allArgOids: parseNullableNumberJsonArray(decodeText(row[5] ?? null)),
|
|
48
|
+
argModes: parseNullableStringJsonArray(decodeText(row[6] ?? null)),
|
|
49
|
+
argNames: parseNullableStringJsonArray(decodeText(row[7] ?? null)),
|
|
50
|
+
returnOid: Number(decodeText(row[8])),
|
|
51
|
+
returnsSet: decodeText(row[9]) === "t",
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
function toEntry(row, schema) {
|
|
55
|
+
const allArgOids = row.allArgOids ?? row.inputArgOids;
|
|
56
|
+
const modes = row.argModes ?? allArgOids.map(() => "i");
|
|
57
|
+
const params = allArgOids.map((oid, i) => {
|
|
58
|
+
const mode = paramMode(modes[i]);
|
|
59
|
+
const resultTsType = outputTsType(oid, schema);
|
|
60
|
+
return {
|
|
61
|
+
mode,
|
|
62
|
+
tsType: mode === "out" || mode === "table" ? resultTsType : inputTsType(oid, schema),
|
|
63
|
+
...(mode === "inout" ? { resultTsType } : {}),
|
|
64
|
+
...(row.argNames?.[i] ? { name: row.argNames[i] } : {}),
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
const output = params.filter((p) => p.mode === "out" || p.mode === "inout" || p.mode === "table");
|
|
68
|
+
return {
|
|
69
|
+
schema: row.schema,
|
|
70
|
+
name: row.name,
|
|
71
|
+
signature: `${row.schema}.${row.name}(${row.identityArguments})`,
|
|
72
|
+
kind: row.kind,
|
|
73
|
+
params: params.map(persistedParam),
|
|
74
|
+
returns: returnTsType(row, output, schema),
|
|
75
|
+
returnsSet: row.returnsSet,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function persistedParam(param) {
|
|
79
|
+
return {
|
|
80
|
+
mode: param.mode,
|
|
81
|
+
tsType: param.tsType,
|
|
82
|
+
...(param.name ? { name: param.name } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function returnTsType(row, output, schema) {
|
|
86
|
+
if (output.length > 0)
|
|
87
|
+
return outputObject(output);
|
|
88
|
+
if (row.kind === "procedure")
|
|
89
|
+
return "void";
|
|
90
|
+
return nullableReturn(schema.tsType(row.returnOid));
|
|
91
|
+
}
|
|
92
|
+
function outputObject(output) {
|
|
93
|
+
const fields = output.map((p, i) => {
|
|
94
|
+
const name = p.name && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(p.name) ? p.name : JSON.stringify(p.name ?? `column${i + 1}`);
|
|
95
|
+
return `${name}: ${nullableReturn(p.resultTsType ?? p.tsType)}`;
|
|
96
|
+
});
|
|
97
|
+
return `{ ${fields.join("; ")} }`;
|
|
98
|
+
}
|
|
99
|
+
function inputTsType(oid, schema) {
|
|
100
|
+
if (JSON_OIDS.has(oid))
|
|
101
|
+
return JSON_INPUT;
|
|
102
|
+
if (JSON_ARRAY_OIDS.has(oid))
|
|
103
|
+
return `(${JSON_INPUT})[]`;
|
|
104
|
+
return schema.tsType(oid);
|
|
105
|
+
}
|
|
106
|
+
function outputTsType(oid, schema) {
|
|
107
|
+
return schema.tsType(oid);
|
|
108
|
+
}
|
|
109
|
+
function nullableReturn(tsType) {
|
|
110
|
+
if (tsType === "void")
|
|
111
|
+
return tsType;
|
|
112
|
+
return `${tsType} | null`;
|
|
113
|
+
}
|
|
114
|
+
function parseNumberJsonArray(raw) {
|
|
115
|
+
if (!raw)
|
|
116
|
+
return [];
|
|
117
|
+
const parsed = JSON.parse(raw);
|
|
118
|
+
return Array.isArray(parsed) ? parsed.map(Number).filter((n) => Number.isFinite(n)) : [];
|
|
119
|
+
}
|
|
120
|
+
function parseNullableNumberJsonArray(raw) {
|
|
121
|
+
if (raw === null)
|
|
122
|
+
return null;
|
|
123
|
+
return parseNumberJsonArray(raw);
|
|
124
|
+
}
|
|
125
|
+
function parseNullableStringJsonArray(raw) {
|
|
126
|
+
if (raw === null)
|
|
127
|
+
return null;
|
|
128
|
+
const parsed = JSON.parse(raw);
|
|
129
|
+
return Array.isArray(parsed) ? parsed.map((v) => (typeof v === "string" ? v : "")) : [];
|
|
130
|
+
}
|
|
131
|
+
function paramMode(raw) {
|
|
132
|
+
switch (raw) {
|
|
133
|
+
case "o": return "out";
|
|
134
|
+
case "b": return "inout";
|
|
135
|
+
case "v": return "variadic";
|
|
136
|
+
case "t": return "table";
|
|
137
|
+
default: return "in";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function functionKind(raw) {
|
|
141
|
+
switch (raw) {
|
|
142
|
+
case "p": return "procedure";
|
|
143
|
+
case "a": return "aggregate";
|
|
144
|
+
case "w": return "window";
|
|
145
|
+
default: return "function";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function userSchemaFilter(alias) {
|
|
149
|
+
return `${alias}.nspname <> 'information_schema' AND ${alias}.nspname NOT LIKE 'pg\\_%' ESCAPE '\\'`;
|
|
150
|
+
}
|
package/dist/src/pg/oids.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const JSON_VALUE = 'import("@onreza/sqlx-js").JsonValue';
|
|
1
2
|
const SCALAR = {
|
|
2
3
|
16: { ts: "boolean" },
|
|
3
4
|
17: { ts: "Uint8Array" },
|
|
@@ -11,7 +12,7 @@ const SCALAR = {
|
|
|
11
12
|
27: { ts: "string" },
|
|
12
13
|
28: { ts: "string" },
|
|
13
14
|
29: { ts: "string" },
|
|
14
|
-
114: { ts:
|
|
15
|
+
114: { ts: JSON_VALUE },
|
|
15
16
|
142: { ts: "string" },
|
|
16
17
|
600: { ts: "string" },
|
|
17
18
|
601: { ts: "string" },
|
|
@@ -41,10 +42,12 @@ const SCALAR = {
|
|
|
41
42
|
2950: { ts: "string" },
|
|
42
43
|
2205: { ts: "string" },
|
|
43
44
|
2206: { ts: "string" },
|
|
45
|
+
2249: { ts: "Record<string, unknown>" },
|
|
46
|
+
2278: { ts: "void" },
|
|
44
47
|
3220: { ts: "string" },
|
|
45
48
|
3614: { ts: "string" },
|
|
46
49
|
3615: { ts: "string" },
|
|
47
|
-
3802: { ts:
|
|
50
|
+
3802: { ts: JSON_VALUE },
|
|
48
51
|
3904: { ts: "string" },
|
|
49
52
|
3906: { ts: "string" },
|
|
50
53
|
3908: { ts: "string" },
|
package/dist/src/pg/param-map.js
CHANGED
|
@@ -12,9 +12,9 @@ export async function buildParamMap(sql) {
|
|
|
12
12
|
else if (stmt.UpdateStmt)
|
|
13
13
|
walkUpdate(stmt.UpdateStmt, targets, dmlBound);
|
|
14
14
|
else if (stmt.SelectStmt)
|
|
15
|
-
|
|
15
|
+
walkSelect(stmt.SelectStmt, targets, dmlBound);
|
|
16
16
|
else if (stmt.DeleteStmt)
|
|
17
|
-
|
|
17
|
+
walkDelete(stmt.DeleteStmt, targets, dmlBound);
|
|
18
18
|
walkForceNullable(stmt, false, forceNullable);
|
|
19
19
|
return { targets, forceNullable, dmlBound };
|
|
20
20
|
}
|
|
@@ -22,6 +22,7 @@ function walkInsert(ins, map, dmlBound) {
|
|
|
22
22
|
const rel = relOf(ins.relation);
|
|
23
23
|
if (!rel)
|
|
24
24
|
return;
|
|
25
|
+
const scope = scopeFromRelationNode(ins.relation, rel);
|
|
25
26
|
const cols = (ins.cols ?? [])
|
|
26
27
|
.map((c) => c?.ResTarget?.name)
|
|
27
28
|
.filter((n) => typeof n === "string");
|
|
@@ -29,82 +30,162 @@ function walkInsert(ins, map, dmlBound) {
|
|
|
29
30
|
for (const row of valuesLists) {
|
|
30
31
|
const items = row?.List?.items ?? [];
|
|
31
32
|
for (let i = 0; i < items.length; i++) {
|
|
32
|
-
const colName = cols[i];
|
|
33
|
-
if (!colName)
|
|
34
|
-
continue;
|
|
35
33
|
const pn = paramNumber(items[i]);
|
|
36
34
|
if (pn !== null) {
|
|
37
|
-
map
|
|
38
|
-
dmlBound.add(pn);
|
|
35
|
+
bindParam(map, dmlBound, pn, insertTarget(rel, cols, i), true);
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
const select = ins.selectStmt?.SelectStmt;
|
|
40
|
+
if (select && !select.valuesLists && Array.isArray(select.targetList)) {
|
|
41
|
+
for (let i = 0; i < select.targetList.length; i++) {
|
|
42
|
+
const pn = paramNumber(select.targetList[i]?.ResTarget?.val);
|
|
43
|
+
if (pn !== null) {
|
|
44
|
+
bindParam(map, dmlBound, pn, insertTarget(rel, cols, i), true);
|
|
45
|
+
}
|
|
45
46
|
}
|
|
47
|
+
walkSelect(select, map, dmlBound);
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
if (ins.returningList)
|
|
50
|
+
walkExpr(ins.returningList, scope, map, dmlBound);
|
|
51
|
+
walkOnConflict(ins.onConflictClause, rel, scope, map, dmlBound);
|
|
52
|
+
walkExpr(ins.whereClause, scope, map, dmlBound);
|
|
53
|
+
}
|
|
54
|
+
function walkOnConflict(conflict, rel, scope, map, dmlBound) {
|
|
55
|
+
if (!conflict || conflict.action !== "ONCONFLICT_UPDATE")
|
|
56
|
+
return;
|
|
57
|
+
for (const rt of conflict.targetList ?? []) {
|
|
58
|
+
const colName = rt?.ResTarget?.name;
|
|
59
|
+
if (typeof colName !== "string")
|
|
60
|
+
continue;
|
|
61
|
+
const pn = paramNumber(rt.ResTarget.val);
|
|
62
|
+
if (pn !== null) {
|
|
63
|
+
bindParam(map, dmlBound, pn, { ...rel, column: colName }, true);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
walkExpr(conflict.whereClause, scope, map, dmlBound);
|
|
48
67
|
}
|
|
49
68
|
function walkUpdate(upd, map, dmlBound) {
|
|
50
69
|
const rel = relOf(upd.relation);
|
|
51
70
|
if (!rel)
|
|
52
71
|
return;
|
|
72
|
+
const scope = scopeFromRelationNode(upd.relation, rel);
|
|
73
|
+
addRangeVars(upd.fromClause ?? [], scope);
|
|
53
74
|
for (const rt of upd.targetList ?? []) {
|
|
54
75
|
const colName = rt?.ResTarget?.name;
|
|
55
76
|
if (typeof colName !== "string")
|
|
56
77
|
continue;
|
|
57
78
|
const pn = paramNumber(rt.ResTarget.val);
|
|
58
79
|
if (pn !== null) {
|
|
59
|
-
map
|
|
60
|
-
dmlBound.add(pn);
|
|
80
|
+
bindParam(map, dmlBound, pn, { ...rel, column: colName }, true);
|
|
61
81
|
}
|
|
62
82
|
}
|
|
63
|
-
|
|
83
|
+
walkExpr(upd.whereClause, scope, map, dmlBound);
|
|
64
84
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
85
|
+
function walkDelete(del, map, dmlBound) {
|
|
86
|
+
const rel = relOf(del.relation);
|
|
87
|
+
if (!rel)
|
|
67
88
|
return;
|
|
89
|
+
const scope = scopeFromRelationNode(del.relation, rel);
|
|
90
|
+
addRangeVars(del.usingClause ?? [], scope);
|
|
91
|
+
walkExpr(del.whereClause, scope, map, dmlBound);
|
|
92
|
+
}
|
|
93
|
+
function walkSelect(select, map, dmlBound) {
|
|
94
|
+
const scope = scopeFromSelect(select);
|
|
95
|
+
walkJoinQuals(select.fromClause ?? [], scope, map, dmlBound);
|
|
96
|
+
walkExpr(select.whereClause, scope, map, dmlBound);
|
|
97
|
+
}
|
|
98
|
+
function walkExpr(node, scope, map, dmlBound) {
|
|
99
|
+
if (!node)
|
|
100
|
+
return;
|
|
101
|
+
if (Array.isArray(node)) {
|
|
102
|
+
for (const item of node)
|
|
103
|
+
walkExpr(item, scope, map, dmlBound);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
68
106
|
if (node.BoolExpr) {
|
|
69
107
|
for (const a of node.BoolExpr.args ?? [])
|
|
70
|
-
|
|
108
|
+
walkExpr(a, scope, map, dmlBound);
|
|
71
109
|
return;
|
|
72
110
|
}
|
|
73
111
|
if (node.A_Expr) {
|
|
74
|
-
collectFromExpr(node,
|
|
112
|
+
collectFromExpr(node, scope, map, dmlBound);
|
|
113
|
+
walkExpr(node.A_Expr.lexpr, scope, map, dmlBound);
|
|
114
|
+
walkExpr(node.A_Expr.rexpr, scope, map, dmlBound);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (node.TypeCast) {
|
|
118
|
+
walkExpr(node.TypeCast.arg, scope, map, dmlBound);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (node.NullTest) {
|
|
122
|
+
walkExpr(node.NullTest.arg, scope, map, dmlBound);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (node.CoalesceExpr) {
|
|
126
|
+
walkExpr(node.CoalesceExpr.args ?? [], scope, map, dmlBound);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (node.FuncCall) {
|
|
130
|
+
walkExpr(node.FuncCall.args ?? [], scope, map, dmlBound);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (node.CaseExpr) {
|
|
134
|
+
walkExpr(node.CaseExpr.arg, scope, map, dmlBound);
|
|
135
|
+
walkExpr(node.CaseExpr.args ?? [], scope, map, dmlBound);
|
|
136
|
+
walkExpr(node.CaseExpr.defresult, scope, map, dmlBound);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (node.CaseWhen) {
|
|
140
|
+
walkExpr(node.CaseWhen.expr, scope, map, dmlBound);
|
|
141
|
+
walkExpr(node.CaseWhen.result, scope, map, dmlBound);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (node.SubLink) {
|
|
145
|
+
const sub = node.SubLink.subselect?.SelectStmt;
|
|
146
|
+
if (sub)
|
|
147
|
+
walkSelect(sub, map, dmlBound);
|
|
148
|
+
walkExpr(node.SubLink.testexpr, scope, map, dmlBound);
|
|
75
149
|
return;
|
|
76
150
|
}
|
|
77
151
|
}
|
|
78
|
-
function collectFromExpr(node,
|
|
79
|
-
if (!node
|
|
152
|
+
function collectFromExpr(node, scope, map, dmlBound) {
|
|
153
|
+
if (!node)
|
|
80
154
|
return;
|
|
81
155
|
if (node.A_Expr) {
|
|
82
156
|
const e = node.A_Expr;
|
|
83
157
|
const opName = e.name?.[0]?.String?.sval;
|
|
84
158
|
if (e.kind === "AEXPR_OP" && opName === "=") {
|
|
85
|
-
tryBind(e.lexpr, e.rexpr,
|
|
86
|
-
tryBind(e.rexpr, e.lexpr,
|
|
159
|
+
tryBind(e.lexpr, e.rexpr, scope, map, dmlBound);
|
|
160
|
+
tryBind(e.rexpr, e.lexpr, scope, map, dmlBound);
|
|
87
161
|
}
|
|
88
162
|
if (e.kind === "AEXPR_IN") {
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
-
const list = Array.isArray(e.rexpr) ? e.rexpr : [];
|
|
163
|
+
const target = targetOfColumnRef(e.lexpr, scope);
|
|
164
|
+
if (target) {
|
|
165
|
+
const list = Array.isArray(e.rexpr) ? e.rexpr : e.rexpr?.List?.items ?? [];
|
|
92
166
|
for (const item of list) {
|
|
93
167
|
const pn = paramNumber(item);
|
|
94
168
|
if (pn !== null)
|
|
95
|
-
map
|
|
169
|
+
bindParam(map, dmlBound, pn, target, false);
|
|
96
170
|
}
|
|
97
171
|
}
|
|
98
172
|
}
|
|
99
173
|
}
|
|
100
174
|
}
|
|
101
|
-
function tryBind(colSide, valSide,
|
|
102
|
-
const
|
|
175
|
+
function tryBind(colSide, valSide, scope, map, dmlBound) {
|
|
176
|
+
const target = targetOfColumnRef(colSide, scope);
|
|
103
177
|
const pn = paramNumber(valSide);
|
|
104
|
-
if (
|
|
105
|
-
map
|
|
178
|
+
if (target && pn !== null)
|
|
179
|
+
bindParam(map, dmlBound, pn, target, false);
|
|
106
180
|
}
|
|
107
|
-
function
|
|
181
|
+
function bindParam(map, dmlBound, pn, target, dml) {
|
|
182
|
+
if (!dml && dmlBound.has(pn))
|
|
183
|
+
return;
|
|
184
|
+
map.set(pn, target);
|
|
185
|
+
if (dml)
|
|
186
|
+
dmlBound.add(pn);
|
|
187
|
+
}
|
|
188
|
+
function stringFields(node) {
|
|
108
189
|
if (!node?.ColumnRef)
|
|
109
190
|
return null;
|
|
110
191
|
const fields = node.ColumnRef.fields;
|
|
@@ -112,7 +193,25 @@ function colNameOf(node) {
|
|
|
112
193
|
return null;
|
|
113
194
|
if (fields.some((f) => f.A_Star !== undefined))
|
|
114
195
|
return null;
|
|
115
|
-
|
|
196
|
+
const out = fields.map((f) => f.String?.sval);
|
|
197
|
+
return out.every((s) => typeof s === "string") ? out : null;
|
|
198
|
+
}
|
|
199
|
+
function targetOfColumnRef(node, scope) {
|
|
200
|
+
const fields = stringFields(node);
|
|
201
|
+
if (!fields)
|
|
202
|
+
return null;
|
|
203
|
+
const column = fields[fields.length - 1];
|
|
204
|
+
if (fields.length === 1) {
|
|
205
|
+
return scope.defaultRel ? { ...scope.defaultRel, column } : null;
|
|
206
|
+
}
|
|
207
|
+
if (fields.length === 2) {
|
|
208
|
+
const qualifier = fields[0];
|
|
209
|
+
const rel = scope.aliases.get(qualifier) ?? scope.relations.find((r) => r.table === qualifier);
|
|
210
|
+
return rel ? { ...rel, column } : { table: qualifier, column };
|
|
211
|
+
}
|
|
212
|
+
const table = fields[fields.length - 2];
|
|
213
|
+
const schema = fields[fields.length - 3];
|
|
214
|
+
return { schema, table, column };
|
|
116
215
|
}
|
|
117
216
|
function paramNumber(node) {
|
|
118
217
|
if (node?.ParamRef && typeof node.ParamRef.number === "number")
|
|
@@ -129,14 +228,56 @@ function relOf(relation) {
|
|
|
129
228
|
return null;
|
|
130
229
|
return { schema: relation.schemaname || undefined, table: name };
|
|
131
230
|
}
|
|
132
|
-
function
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
231
|
+
function insertTarget(rel, cols, index) {
|
|
232
|
+
const column = cols[index];
|
|
233
|
+
return column ? { ...rel, column } : { ...rel, columnIndex: index + 1 };
|
|
234
|
+
}
|
|
235
|
+
function scopeFromSelect(select) {
|
|
236
|
+
const scope = scopeFromRelations([], null);
|
|
237
|
+
addRangeVars(select?.fromClause ?? [], scope);
|
|
238
|
+
if (scope.relations.length === 1)
|
|
239
|
+
scope.defaultRel = scope.relations[0];
|
|
240
|
+
return scope;
|
|
241
|
+
}
|
|
242
|
+
function scopeFromRelations(relations, defaultRel) {
|
|
243
|
+
const scope = { aliases: new Map(), relations: [], defaultRel };
|
|
244
|
+
for (const rel of relations)
|
|
245
|
+
addRelation(scope, rel, rel.table);
|
|
246
|
+
return scope;
|
|
247
|
+
}
|
|
248
|
+
function scopeFromRelationNode(relation, rel) {
|
|
249
|
+
const scope = scopeFromRelations([rel], rel);
|
|
250
|
+
const alias = relation?.alias?.aliasname;
|
|
251
|
+
if (typeof alias === "string")
|
|
252
|
+
scope.aliases.set(alias, rel);
|
|
253
|
+
return scope;
|
|
254
|
+
}
|
|
255
|
+
function addRelation(scope, rel, alias) {
|
|
256
|
+
scope.relations.push(rel);
|
|
257
|
+
scope.aliases.set(alias, rel);
|
|
258
|
+
}
|
|
259
|
+
function addRangeVars(nodes, scope) {
|
|
260
|
+
for (const node of nodes) {
|
|
261
|
+
if (node?.RangeVar) {
|
|
262
|
+
const rel = relOf(node.RangeVar);
|
|
263
|
+
if (!rel)
|
|
264
|
+
continue;
|
|
265
|
+
const alias = node.RangeVar.alias?.aliasname ?? rel.table;
|
|
266
|
+
addRelation(scope, rel, alias);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (node?.JoinExpr) {
|
|
270
|
+
addRangeVars([node.JoinExpr.larg, node.JoinExpr.rarg], scope);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function walkJoinQuals(nodes, scope, map, dmlBound) {
|
|
275
|
+
for (const node of nodes) {
|
|
276
|
+
if (!node?.JoinExpr)
|
|
277
|
+
continue;
|
|
278
|
+
walkExpr(node.JoinExpr.quals, scope, map, dmlBound);
|
|
279
|
+
walkJoinQuals([node.JoinExpr.larg, node.JoinExpr.rarg], scope, map, dmlBound);
|
|
280
|
+
}
|
|
140
281
|
}
|
|
141
282
|
function walkForceNullable(node, forceContext, out) {
|
|
142
283
|
if (node === null || node === undefined)
|
package/dist/src/pg/schema.d.ts
CHANGED
package/dist/src/pg/schema.js
CHANGED