@dbsp/adapter-pgsql 1.10.1 → 1.11.1
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/dist/index.d.ts +12 -2
- package/dist/index.js +607 -179
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -95,7 +95,46 @@ var init_naming_plugin = __esm({
|
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
// src/validate.ts
|
|
98
|
+
function assertString(value, context) {
|
|
99
|
+
if (typeof value !== "string") {
|
|
100
|
+
throw new Error(`${context}: expected a string, received ${typeof value}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function assertNumericLiteral(value, context) {
|
|
104
|
+
if (typeof value !== "number" && typeof value !== "string") {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`${context}: expected a finite number or numeric string, received ${typeof value}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const literal4 = typeof value === "number" ? String(value) : value;
|
|
110
|
+
if (typeof value === "number" && !Number.isFinite(value) || !SAFE_DECIMAL_LITERAL.test(literal4)) {
|
|
111
|
+
throw new Error(`${context}: unsafe numeric literal "${literal4}"`);
|
|
112
|
+
}
|
|
113
|
+
return literal4;
|
|
114
|
+
}
|
|
115
|
+
function formatStorageParameterValue(value, context) {
|
|
116
|
+
if (typeof value !== "number" && typeof value !== "string") {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`${context}: expected a string or number, received ${typeof value}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
if (typeof value === "number") {
|
|
122
|
+
return assertNumericLiteral(value, context);
|
|
123
|
+
}
|
|
124
|
+
if (value.includes("\0")) {
|
|
125
|
+
throw new Error(`${context}: contains null byte`);
|
|
126
|
+
}
|
|
127
|
+
if (SAFE_DECIMAL_LITERAL.test(value) || SAFE_STORAGE_BOOLEAN_LITERAL.test(value) || SAFE_SIMPLE_IDENTIFIER_VALUE.test(value) && !isReservedKeyword(value) && value.toLowerCase() !== "null") {
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
131
|
+
}
|
|
98
132
|
function validateIdentifier(value, type) {
|
|
133
|
+
if (typeof value !== "string") {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Invalid ${type} identifier: expected a string, received ${typeof value}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
99
138
|
if (!value || value.length === 0) {
|
|
100
139
|
throw new InvalidIdentifierError(value, type, "cannot be empty");
|
|
101
140
|
}
|
|
@@ -171,7 +210,12 @@ function validateIdentifiers(identifiers) {
|
|
|
171
210
|
function sanitizeForDisplay(value) {
|
|
172
211
|
return value.replace(/[\x00-\x1f\x7f]/g, "?").slice(0, 100);
|
|
173
212
|
}
|
|
213
|
+
function sanitizeCommentText(value) {
|
|
214
|
+
assertString(value, "SQL comment text");
|
|
215
|
+
return value.replace(/[\x00-\x1f\x7f]/g, "");
|
|
216
|
+
}
|
|
174
217
|
function validateExtensionName(name, context = "extension") {
|
|
218
|
+
assertString(name, `Invalid ${context} identifier`);
|
|
175
219
|
if (!name || name.length === 0) {
|
|
176
220
|
throw new InvalidIdentifierError(name, context, "cannot be empty");
|
|
177
221
|
}
|
|
@@ -257,6 +301,7 @@ function validateExtensionName(name, context = "extension") {
|
|
|
257
301
|
}
|
|
258
302
|
}
|
|
259
303
|
function validateCollationName(name, context = "collation") {
|
|
304
|
+
assertString(name, `Invalid ${context} identifier`);
|
|
260
305
|
if (!name || name.length === 0) {
|
|
261
306
|
throw new InvalidIdentifierError(name, context, "cannot be empty");
|
|
262
307
|
}
|
|
@@ -349,13 +394,106 @@ function validateCollationName(name, context = "collation") {
|
|
|
349
394
|
}
|
|
350
395
|
}
|
|
351
396
|
function validateSqlExpression(sql, context) {
|
|
397
|
+
assertString(sql, `Unsafe SQL expression in ${context}`);
|
|
352
398
|
if (/[;]|--|\/\*|\*\/|\$\$|\\/.test(sql)) {
|
|
353
399
|
throw new Error(
|
|
354
400
|
`Unsafe SQL expression in ${context}: contains forbidden characters (;, --, /*, */, "$$" (dollar-quoted strings), \\). Value: "${sql}"`
|
|
355
401
|
);
|
|
356
402
|
}
|
|
357
403
|
}
|
|
404
|
+
function validateCheckExpression(sql, context) {
|
|
405
|
+
if (typeof sql !== "string") {
|
|
406
|
+
throw new Error(
|
|
407
|
+
`Unsafe SQL expression in ${context}: expected a string, received ${typeof sql}`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
let i = 0;
|
|
411
|
+
let singleQuoted = false;
|
|
412
|
+
let dollarQuoteDelimiter;
|
|
413
|
+
while (i < sql.length) {
|
|
414
|
+
if (singleQuoted) {
|
|
415
|
+
if (sql[i] === "'") {
|
|
416
|
+
if (sql[i + 1] === "'") {
|
|
417
|
+
i += 2;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
singleQuoted = false;
|
|
421
|
+
}
|
|
422
|
+
i++;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (dollarQuoteDelimiter) {
|
|
426
|
+
if (sql.startsWith(dollarQuoteDelimiter, i)) {
|
|
427
|
+
i += dollarQuoteDelimiter.length;
|
|
428
|
+
dollarQuoteDelimiter = void 0;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
i++;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (sql[i] === "'") {
|
|
435
|
+
singleQuoted = true;
|
|
436
|
+
i++;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const dollarDelimiter = readDollarQuoteDelimiter(sql, i);
|
|
440
|
+
if (dollarDelimiter) {
|
|
441
|
+
dollarQuoteDelimiter = dollarDelimiter;
|
|
442
|
+
i += dollarDelimiter.length;
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const forbiddenToken = forbiddenCheckTokenAt(sql, i);
|
|
446
|
+
if (forbiddenToken) {
|
|
447
|
+
throw new Error(
|
|
448
|
+
`Unsafe SQL expression in ${context}: contains forbidden token "${forbiddenToken}" outside string literal. Value: "${sql}"`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
i++;
|
|
452
|
+
}
|
|
453
|
+
if (singleQuoted) {
|
|
454
|
+
throw new Error(
|
|
455
|
+
`Unsafe SQL expression in ${context}: unterminated single-quoted string literal. Value: "${sql}"`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
if (dollarQuoteDelimiter) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
`Unsafe SQL expression in ${context}: unterminated dollar-quoted string literal ${dollarQuoteDelimiter}. Value: "${sql}"`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function forbiddenCheckTokenAt(sql, index) {
|
|
465
|
+
const ch = sql[index];
|
|
466
|
+
if (ch === ";" || ch === "\\") return ch;
|
|
467
|
+
const pair = sql.slice(index, index + 2);
|
|
468
|
+
if (pair === "--" || pair === "/*" || pair === "*/") return pair;
|
|
469
|
+
return void 0;
|
|
470
|
+
}
|
|
471
|
+
function readDollarQuoteDelimiter(sql, index) {
|
|
472
|
+
if (sql[index] !== "$") return void 0;
|
|
473
|
+
const previous = index > 0 ? sql[index - 1] : void 0;
|
|
474
|
+
if (previous && isIdentifierContinuationBeforeDollarQuote(previous)) {
|
|
475
|
+
return void 0;
|
|
476
|
+
}
|
|
477
|
+
if (sql[index + 1] === "$") return "$$";
|
|
478
|
+
if (!isDollarQuoteTagStart(sql[index + 1])) return void 0;
|
|
479
|
+
let cursor = index + 2;
|
|
480
|
+
while (isDollarQuoteTagContinuation(sql[cursor])) {
|
|
481
|
+
cursor++;
|
|
482
|
+
}
|
|
483
|
+
if (sql[cursor] !== "$") return void 0;
|
|
484
|
+
return sql.slice(index, cursor + 1);
|
|
485
|
+
}
|
|
486
|
+
function isIdentifierContinuationBeforeDollarQuote(ch) {
|
|
487
|
+
return /[A-Za-z0-9_$]/.test(ch) || (ch.codePointAt(0) ?? 0) > 127;
|
|
488
|
+
}
|
|
489
|
+
function isDollarQuoteTagStart(ch) {
|
|
490
|
+
return ch !== void 0 && /[A-Za-z_]/.test(ch);
|
|
491
|
+
}
|
|
492
|
+
function isDollarQuoteTagContinuation(ch) {
|
|
493
|
+
return ch !== void 0 && /[A-Za-z0-9_]/.test(ch);
|
|
494
|
+
}
|
|
358
495
|
function validateDbTypeName(type) {
|
|
496
|
+
assertString(type, "Unsafe database type name");
|
|
359
497
|
if (!SAFE_TYPE_PATTERN.test(type)) {
|
|
360
498
|
throw new Error(
|
|
361
499
|
`Unsafe database type name: "${type}". Must match PostgreSQL type name rules.`
|
|
@@ -363,7 +501,7 @@ function validateDbTypeName(type) {
|
|
|
363
501
|
}
|
|
364
502
|
return type;
|
|
365
503
|
}
|
|
366
|
-
var SQL_RESERVED_KEYWORDS, InvalidIdentifierError, SAFE_TYPE_PATTERN;
|
|
504
|
+
var SQL_RESERVED_KEYWORDS, InvalidIdentifierError, SAFE_DECIMAL_LITERAL, SAFE_STORAGE_BOOLEAN_LITERAL, SAFE_SIMPLE_IDENTIFIER_VALUE, SAFE_TYPE_PATTERN;
|
|
367
505
|
var init_validate = __esm({
|
|
368
506
|
"src/validate.ts"() {
|
|
369
507
|
"use strict";
|
|
@@ -482,6 +620,9 @@ var init_validate = __esm({
|
|
|
482
620
|
identifierType;
|
|
483
621
|
reason;
|
|
484
622
|
};
|
|
623
|
+
SAFE_DECIMAL_LITERAL = /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/;
|
|
624
|
+
SAFE_STORAGE_BOOLEAN_LITERAL = /^(?:true|false|on|off)$/i;
|
|
625
|
+
SAFE_SIMPLE_IDENTIFIER_VALUE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
485
626
|
SAFE_TYPE_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_ ]*(\(\d+(,\s*\d+)?\))?(\[\])?$/;
|
|
486
627
|
}
|
|
487
628
|
});
|
|
@@ -1126,7 +1267,7 @@ var init_param_value = __esm({
|
|
|
1126
1267
|
});
|
|
1127
1268
|
|
|
1128
1269
|
// src/handlers/expression/case-value.ts
|
|
1129
|
-
function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler) {
|
|
1270
|
+
function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler, expressionHandler) {
|
|
1130
1271
|
if (value === null || value === void 0) {
|
|
1131
1272
|
return nullConstNode();
|
|
1132
1273
|
}
|
|
@@ -1160,7 +1301,8 @@ function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler
|
|
|
1160
1301
|
schema,
|
|
1161
1302
|
naming,
|
|
1162
1303
|
state,
|
|
1163
|
-
nestedCaseHandler
|
|
1304
|
+
nestedCaseHandler,
|
|
1305
|
+
expressionHandler
|
|
1164
1306
|
);
|
|
1165
1307
|
const right = resolveCaseValue(
|
|
1166
1308
|
expr.right,
|
|
@@ -1168,7 +1310,8 @@ function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler
|
|
|
1168
1310
|
schema,
|
|
1169
1311
|
naming,
|
|
1170
1312
|
state,
|
|
1171
|
-
nestedCaseHandler
|
|
1313
|
+
nestedCaseHandler,
|
|
1314
|
+
expressionHandler
|
|
1172
1315
|
);
|
|
1173
1316
|
return {
|
|
1174
1317
|
A_Expr: {
|
|
@@ -1186,15 +1329,29 @@ function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler
|
|
|
1186
1329
|
}
|
|
1187
1330
|
// falls through
|
|
1188
1331
|
default: {
|
|
1332
|
+
if (expressionHandler && typeof expr.kind === "string" && EXPRESSION_HANDLER_KINDS.has(expr.kind)) {
|
|
1333
|
+
return expressionHandler(expr);
|
|
1334
|
+
}
|
|
1189
1335
|
return bindParameter(value, state);
|
|
1190
1336
|
}
|
|
1191
1337
|
}
|
|
1192
1338
|
}
|
|
1339
|
+
var EXPRESSION_HANDLER_KINDS;
|
|
1193
1340
|
var init_case_value = __esm({
|
|
1194
1341
|
"src/handlers/expression/case-value.ts"() {
|
|
1195
1342
|
"use strict";
|
|
1196
1343
|
init_ast_helpers();
|
|
1197
1344
|
init_param_value();
|
|
1345
|
+
EXPRESSION_HANDLER_KINDS = /* @__PURE__ */ new Set([
|
|
1346
|
+
"customOp",
|
|
1347
|
+
"customFn",
|
|
1348
|
+
"ref",
|
|
1349
|
+
"cast",
|
|
1350
|
+
"unary",
|
|
1351
|
+
"array",
|
|
1352
|
+
"subquery",
|
|
1353
|
+
"relationColumn"
|
|
1354
|
+
]);
|
|
1198
1355
|
}
|
|
1199
1356
|
});
|
|
1200
1357
|
|
|
@@ -6659,7 +6816,21 @@ var init_arithmetic = __esm({
|
|
|
6659
6816
|
// src/handlers/expression/case.ts
|
|
6660
6817
|
function resolveCaseValue2(value, ctx, state) {
|
|
6661
6818
|
const alias = ctx.currentAlias ?? ctx.rootTable;
|
|
6662
|
-
return resolveCaseValue(
|
|
6819
|
+
return resolveCaseValue(
|
|
6820
|
+
value,
|
|
6821
|
+
alias,
|
|
6822
|
+
void 0,
|
|
6823
|
+
ctx.naming,
|
|
6824
|
+
state,
|
|
6825
|
+
void 0,
|
|
6826
|
+
// Handler-level path: renders every expression kind via the shared
|
|
6827
|
+
// expression compiler. It does NOT apply the customFn FILTER modifier
|
|
6828
|
+
// (agg_filter) — that requires compiler-private compileFilterCondition,
|
|
6829
|
+
// which cannot be reached here without a circular dependency. The primary
|
|
6830
|
+
// DX caseWhen path (PlanCompiler.compileCustomExpressionNode) filter-patches
|
|
6831
|
+
// correctly; use it for filtered aggregates as CASE branches.
|
|
6832
|
+
(expr) => compileExpressionIntent(expr, ctx, state)
|
|
6833
|
+
);
|
|
6663
6834
|
}
|
|
6664
6835
|
var caseHandler, simpleCaseHandler;
|
|
6665
6836
|
var init_case = __esm({
|
|
@@ -6668,6 +6839,7 @@ var init_case = __esm({
|
|
|
6668
6839
|
init_ast_helpers();
|
|
6669
6840
|
init_param_intent();
|
|
6670
6841
|
init_case_value();
|
|
6842
|
+
init_custom();
|
|
6671
6843
|
init_param_value();
|
|
6672
6844
|
caseHandler = {
|
|
6673
6845
|
types: ["case", "CASE", "caseWhen"],
|
|
@@ -9366,6 +9538,24 @@ function renumberNode(value, offset) {
|
|
|
9366
9538
|
}
|
|
9367
9539
|
return result;
|
|
9368
9540
|
}
|
|
9541
|
+
function maxParamRefNumber(value) {
|
|
9542
|
+
if (value === null || typeof value !== "object") return 0;
|
|
9543
|
+
if (Array.isArray(value)) {
|
|
9544
|
+
let max2 = 0;
|
|
9545
|
+
for (const item of value) max2 = Math.max(max2, maxParamRefNumber(item));
|
|
9546
|
+
return max2;
|
|
9547
|
+
}
|
|
9548
|
+
const obj = value;
|
|
9549
|
+
if ("ParamRef" in obj && obj.ParamRef !== null && typeof obj.ParamRef === "object") {
|
|
9550
|
+
const n = obj.ParamRef.number;
|
|
9551
|
+
return typeof n === "number" ? n : 0;
|
|
9552
|
+
}
|
|
9553
|
+
let max = 0;
|
|
9554
|
+
for (const key of Object.keys(obj)) {
|
|
9555
|
+
max = Math.max(max, maxParamRefNumber(obj[key]));
|
|
9556
|
+
}
|
|
9557
|
+
return max;
|
|
9558
|
+
}
|
|
9369
9559
|
var includeHandlersInitialized = false;
|
|
9370
9560
|
function ensureIncludeHandlersRegistered() {
|
|
9371
9561
|
if (includeHandlersInitialized) return;
|
|
@@ -10400,7 +10590,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10400
10590
|
}
|
|
10401
10591
|
case "selectExpression": {
|
|
10402
10592
|
if (decision.expressionType === "case") {
|
|
10403
|
-
const caseNode = this.compileCaseExpression(decision);
|
|
10593
|
+
const caseNode = this.compileCaseExpression(decision, plan);
|
|
10404
10594
|
const alias = decision.alias;
|
|
10405
10595
|
targetList.push({
|
|
10406
10596
|
ResTarget: {
|
|
@@ -10413,34 +10603,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10413
10603
|
}
|
|
10414
10604
|
case "selectCustomExpression": {
|
|
10415
10605
|
const exprIntent = decision.expressionIntent;
|
|
10416
|
-
const
|
|
10417
|
-
const ctx = {
|
|
10418
|
-
...this.createHandlerContext(plan, plan.rootTable),
|
|
10419
|
-
compileSubquery(query, paramOffset) {
|
|
10420
|
-
return outerThis.compileExpressionSubquery(query, paramOffset);
|
|
10421
|
-
}
|
|
10422
|
-
};
|
|
10423
|
-
const state = this.createHandlerState();
|
|
10424
|
-
const node = compileExpressionIntent(exprIntent, ctx, state);
|
|
10425
|
-
if (exprIntent.kind === "customFn" && exprIntent.filter) {
|
|
10426
|
-
const filterIntent = exprIntent.filter;
|
|
10427
|
-
const filterDecision = convertWhereCondition(
|
|
10428
|
-
filterIntent,
|
|
10429
|
-
plan.rootTable
|
|
10430
|
-
);
|
|
10431
|
-
if (filterDecision) {
|
|
10432
|
-
const filterNode = compileFilterCondition(
|
|
10433
|
-
filterDecision,
|
|
10434
|
-
createWhereDispatcher(),
|
|
10435
|
-
ctx,
|
|
10436
|
-
state
|
|
10437
|
-
);
|
|
10438
|
-
if (filterNode && "FuncCall" in node) {
|
|
10439
|
-
node.FuncCall.agg_filter = filterNode;
|
|
10440
|
-
}
|
|
10441
|
-
}
|
|
10442
|
-
}
|
|
10443
|
-
this.state.paramIndex = state.paramIndex;
|
|
10606
|
+
const node = this.compileCustomExpressionNode(exprIntent, plan);
|
|
10444
10607
|
const alias = decision.alias || decision.column || void 0;
|
|
10445
10608
|
targetList.push({
|
|
10446
10609
|
ResTarget: {
|
|
@@ -10707,14 +10870,28 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10707
10870
|
*/
|
|
10708
10871
|
compileJoinDecision(decision, plan, from) {
|
|
10709
10872
|
if (isPrecompiledJoinDecision(decision)) {
|
|
10710
|
-
|
|
10873
|
+
const isBatchValues = isBatchValuesJoinDecision(decision);
|
|
10874
|
+
if (isBatchValues) {
|
|
10711
10875
|
for (const p of decision.batchValuesParams) {
|
|
10712
10876
|
this.state.parameters.push(p);
|
|
10713
10877
|
}
|
|
10714
10878
|
this.state.paramIndex = this.state.parameters.length;
|
|
10715
10879
|
}
|
|
10716
10880
|
const jRarg = decision.joinRarg;
|
|
10717
|
-
|
|
10881
|
+
let jOn = decision.joinOnNode;
|
|
10882
|
+
if (!isBatchValues && decision.joinOnParams?.length) {
|
|
10883
|
+
if (maxParamRefNumber(decision.joinOnNode) > decision.joinOnParams.length) {
|
|
10884
|
+
throw new Error(
|
|
10885
|
+
"Table-mode JOIN ON clauses support only inline param() values; externally pre-assigned or bound parameters in an ON clause are not supported."
|
|
10886
|
+
);
|
|
10887
|
+
}
|
|
10888
|
+
const offset = this.state.parameters.length;
|
|
10889
|
+
jOn = renumberParamRefsInAst(decision.joinOnNode, offset);
|
|
10890
|
+
for (const p of decision.joinOnParams) {
|
|
10891
|
+
this.state.parameters.push(p);
|
|
10892
|
+
}
|
|
10893
|
+
this.state.paramIndex = this.state.parameters.length;
|
|
10894
|
+
}
|
|
10718
10895
|
from[0] = decision.joinType === "left" ? leftJoin(from[0], jRarg, jOn) : innerJoin(from[0], jRarg, jOn);
|
|
10719
10896
|
} else {
|
|
10720
10897
|
from[0] = this.compileJoin(decision, plan, from[0]);
|
|
@@ -10747,11 +10924,9 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10747
10924
|
}
|
|
10748
10925
|
if (decision.column) {
|
|
10749
10926
|
return sortBy(
|
|
10750
|
-
|
|
10927
|
+
this.compileRelationAwareColumnRef(
|
|
10751
10928
|
decision.column,
|
|
10752
|
-
decision.table
|
|
10753
|
-
void 0,
|
|
10754
|
-
this.naming
|
|
10929
|
+
decision.table
|
|
10755
10930
|
),
|
|
10756
10931
|
decision.direction ?? "ASC",
|
|
10757
10932
|
decision.nulls ?? "DEFAULT"
|
|
@@ -11010,7 +11185,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11010
11185
|
// --------------------------------------------------------------------------
|
|
11011
11186
|
// CASE Expression Compilation
|
|
11012
11187
|
// --------------------------------------------------------------------------
|
|
11013
|
-
compileCaseExpression(decision) {
|
|
11188
|
+
compileCaseExpression(decision, plan) {
|
|
11014
11189
|
const conditions = decision.conditions;
|
|
11015
11190
|
const elseValue = decision.value;
|
|
11016
11191
|
if (!conditions || conditions.length === 0) {
|
|
@@ -11018,7 +11193,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11018
11193
|
}
|
|
11019
11194
|
const args = conditions.map((cond) => {
|
|
11020
11195
|
const whenExpr = this.dispatchWhere(cond.when);
|
|
11021
|
-
const thenResult = this.compileCaseValue(cond.then);
|
|
11196
|
+
const thenResult = this.compileCaseValue(cond.then, plan);
|
|
11022
11197
|
return {
|
|
11023
11198
|
CaseWhen: {
|
|
11024
11199
|
expr: whenExpr,
|
|
@@ -11028,7 +11203,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11028
11203
|
});
|
|
11029
11204
|
let defresult;
|
|
11030
11205
|
if (elseValue !== void 0) {
|
|
11031
|
-
defresult = this.compileCaseValue(elseValue);
|
|
11206
|
+
defresult = this.compileCaseValue(elseValue, plan);
|
|
11032
11207
|
}
|
|
11033
11208
|
return {
|
|
11034
11209
|
CaseExpr: {
|
|
@@ -11041,26 +11216,66 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11041
11216
|
* Compile a CASE THEN/ELSE value based on its ExpressionIntent kind.
|
|
11042
11217
|
* Delegates to shared resolveCaseValue with nested CASE support.
|
|
11043
11218
|
*/
|
|
11044
|
-
compileCaseValue(value) {
|
|
11219
|
+
compileCaseValue(value, plan) {
|
|
11045
11220
|
return resolveCaseValue(
|
|
11046
11221
|
value,
|
|
11047
11222
|
this.currentRootTable,
|
|
11048
11223
|
void 0,
|
|
11049
11224
|
this.naming,
|
|
11050
11225
|
this.state,
|
|
11051
|
-
(expr) =>
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11226
|
+
(expr) => {
|
|
11227
|
+
const nestedDecision = {
|
|
11228
|
+
type: "selectExpression",
|
|
11229
|
+
expressionType: "case",
|
|
11230
|
+
conditions: expr.when.map((wc) => ({
|
|
11231
|
+
when: wc.condition,
|
|
11232
|
+
// biome-ignore lint/suspicious/noThenProperty: intentional
|
|
11233
|
+
then: wc.result
|
|
11234
|
+
})),
|
|
11235
|
+
value: expr.else,
|
|
11236
|
+
table: this.currentRootTable
|
|
11237
|
+
};
|
|
11238
|
+
return this.compileCaseExpression(nestedDecision, plan);
|
|
11239
|
+
},
|
|
11240
|
+
(expr) => this.compileCustomExpressionNode(
|
|
11241
|
+
expr,
|
|
11242
|
+
plan
|
|
11243
|
+
)
|
|
11062
11244
|
);
|
|
11063
11245
|
}
|
|
11246
|
+
/**
|
|
11247
|
+
* Compile a custom ExpressionIntent (customFn, customOp, ref, cast, unary,
|
|
11248
|
+
* array, function, subquery, …) to an AST node, applying the customFn FILTER
|
|
11249
|
+
* clause. Shared by the `selectCustomExpression` target path and CASE
|
|
11250
|
+
* THEN/ELSE values so both render the full expression surface identically
|
|
11251
|
+
* (every expression kind + FILTER), rather than one path silently binding
|
|
11252
|
+
* expressions as parameters or dropping FILTER.
|
|
11253
|
+
*/
|
|
11254
|
+
compileCustomExpressionNode(exprIntent, plan) {
|
|
11255
|
+
const ctx = this.createHandlerContext(plan, plan.rootTable);
|
|
11256
|
+
const state = this.createHandlerState();
|
|
11257
|
+
const node = compileExpressionIntent(exprIntent, ctx, state);
|
|
11258
|
+
if (exprIntent.kind === "customFn" && exprIntent.filter) {
|
|
11259
|
+
const filterIntent = exprIntent.filter;
|
|
11260
|
+
const filterDecision = convertWhereCondition(
|
|
11261
|
+
filterIntent,
|
|
11262
|
+
plan.rootTable
|
|
11263
|
+
);
|
|
11264
|
+
if (filterDecision) {
|
|
11265
|
+
const filterNode = compileFilterCondition(
|
|
11266
|
+
filterDecision,
|
|
11267
|
+
createWhereDispatcher(),
|
|
11268
|
+
ctx,
|
|
11269
|
+
state
|
|
11270
|
+
);
|
|
11271
|
+
if (filterNode && "FuncCall" in node) {
|
|
11272
|
+
node.FuncCall.agg_filter = filterNode;
|
|
11273
|
+
}
|
|
11274
|
+
}
|
|
11275
|
+
}
|
|
11276
|
+
this.state.paramIndex = state.paramIndex;
|
|
11277
|
+
return node;
|
|
11278
|
+
}
|
|
11064
11279
|
// --------------------------------------------------------------------------
|
|
11065
11280
|
// INSERT Compilation
|
|
11066
11281
|
// --------------------------------------------------------------------------
|
|
@@ -11263,6 +11478,9 @@ function compilePlan(plan, options) {
|
|
|
11263
11478
|
init_naming_plugin();
|
|
11264
11479
|
init_validate();
|
|
11265
11480
|
|
|
11481
|
+
// src/ddl/phases/comments.ts
|
|
11482
|
+
init_validate();
|
|
11483
|
+
|
|
11266
11484
|
// src/ddl/phases/types.ts
|
|
11267
11485
|
function sup(caps, flag) {
|
|
11268
11486
|
return !caps || flag === true;
|
|
@@ -11283,6 +11501,7 @@ var VALID_INDEX_METHODS = /* @__PURE__ */ new Set([
|
|
|
11283
11501
|
"bloom"
|
|
11284
11502
|
]);
|
|
11285
11503
|
function validateIndexMethod(method, context = "index method") {
|
|
11504
|
+
assertString(method, `Invalid ${context}`);
|
|
11286
11505
|
if (!VALID_INDEX_METHODS.has(method)) {
|
|
11287
11506
|
throw new Error(
|
|
11288
11507
|
`Invalid ${context}: "${method}". Must be one of: ${[...VALID_INDEX_METHODS].join(", ")}`
|
|
@@ -11290,6 +11509,7 @@ function validateIndexMethod(method, context = "index method") {
|
|
|
11290
11509
|
}
|
|
11291
11510
|
}
|
|
11292
11511
|
function validateEnumLabel(value, context = "enum label") {
|
|
11512
|
+
assertString(value, `Invalid ${context}`);
|
|
11293
11513
|
if (/\x00/.test(value)) {
|
|
11294
11514
|
throw new Error(
|
|
11295
11515
|
`Invalid ${context}: contains NUL byte (\\x00) which would be silently truncated by PostgreSQL`
|
|
@@ -11314,6 +11534,7 @@ function quoteCollation(name) {
|
|
|
11314
11534
|
return `"${name}"`;
|
|
11315
11535
|
}
|
|
11316
11536
|
function quoteRoleName(name) {
|
|
11537
|
+
assertString(name, "quoteRoleName: role name");
|
|
11317
11538
|
if (!name) {
|
|
11318
11539
|
throw new Error("quoteRoleName: role name must not be empty");
|
|
11319
11540
|
}
|
|
@@ -11380,17 +11601,21 @@ function generateCommentsPhase(ctx) {
|
|
|
11380
11601
|
}
|
|
11381
11602
|
const statements = [];
|
|
11382
11603
|
for (const table of tables) {
|
|
11383
|
-
|
|
11604
|
+
const tableComment = table.comment;
|
|
11605
|
+
if (tableComment) {
|
|
11606
|
+
assertString(tableComment, "table comment");
|
|
11384
11607
|
const qualifiedTable = qualifyTableIdent(table.name, schemaName, naming);
|
|
11385
11608
|
statements.push(
|
|
11386
|
-
`COMMENT ON TABLE ${qualifiedTable} IS '${
|
|
11609
|
+
`COMMENT ON TABLE ${qualifiedTable} IS '${tableComment.replace(/'/g, "''")}';`
|
|
11387
11610
|
);
|
|
11388
11611
|
}
|
|
11389
11612
|
for (const col of table.columns) {
|
|
11390
|
-
|
|
11613
|
+
const columnComment = col.comment;
|
|
11614
|
+
if (columnComment) {
|
|
11615
|
+
assertString(columnComment, "column comment");
|
|
11391
11616
|
const qualifiedTable = qualifyTableIdent(table.name, schemaName, naming);
|
|
11392
11617
|
statements.push(
|
|
11393
|
-
`COMMENT ON COLUMN ${qualifiedTable}.${quoteIdent2(naming.toDatabase(col.name))} IS '${
|
|
11618
|
+
`COMMENT ON COLUMN ${qualifiedTable}.${quoteIdent2(naming.toDatabase(col.name))} IS '${columnComment.replace(/'/g, "''")}';`
|
|
11394
11619
|
);
|
|
11395
11620
|
}
|
|
11396
11621
|
}
|
|
@@ -11415,9 +11640,10 @@ function generateConstraintsPhase(ctx) {
|
|
|
11415
11640
|
for (const check of table.checkConstraints ?? []) {
|
|
11416
11641
|
const qualifiedTable = qualifyTableIdent(table.name, schemaName, naming);
|
|
11417
11642
|
const constraintName = quoteIdent2(check.name);
|
|
11418
|
-
|
|
11643
|
+
const expression = check.expression;
|
|
11644
|
+
validateCheckExpression(expression, "check constraint expression");
|
|
11419
11645
|
statements.push(
|
|
11420
|
-
`ALTER TABLE ${qualifiedTable} ADD CONSTRAINT ${constraintName} ${
|
|
11646
|
+
`ALTER TABLE ${qualifiedTable} ADD CONSTRAINT ${constraintName} ${expression};`
|
|
11421
11647
|
);
|
|
11422
11648
|
}
|
|
11423
11649
|
}
|
|
@@ -11600,12 +11826,29 @@ function mapOnDeleteAction(action) {
|
|
|
11600
11826
|
function qualifyTable(table, schemaName) {
|
|
11601
11827
|
return schemaName ? `${quoteIdent2(schemaName, "schema")}.${quoteIdent2(table, "table")}` : quoteIdent2(table, "table");
|
|
11602
11828
|
}
|
|
11829
|
+
function buildDoBlock(body) {
|
|
11830
|
+
const delimiter = chooseDoBlockDelimiter(body);
|
|
11831
|
+
return `DO ${delimiter} ${body} ${delimiter};`;
|
|
11832
|
+
}
|
|
11833
|
+
function chooseDoBlockDelimiter(body) {
|
|
11834
|
+
if (!body.includes("$$")) return "$$";
|
|
11835
|
+
let suffix = 0;
|
|
11836
|
+
let delimiter = "$dbsp_check$";
|
|
11837
|
+
while (body.includes(delimiter)) {
|
|
11838
|
+
suffix++;
|
|
11839
|
+
delimiter = `$dbsp_check_${suffix}$`;
|
|
11840
|
+
}
|
|
11841
|
+
return delimiter;
|
|
11842
|
+
}
|
|
11603
11843
|
function pkName(table) {
|
|
11604
11844
|
return `pk_${table}`;
|
|
11605
11845
|
}
|
|
11606
11846
|
function fkName(table, columns) {
|
|
11607
11847
|
return `fk_${table}_${columns.join("_")}`;
|
|
11608
11848
|
}
|
|
11849
|
+
function uniqueName(table, column) {
|
|
11850
|
+
return `${table}_${column}_key`;
|
|
11851
|
+
}
|
|
11609
11852
|
function idxName(table, columns, customName) {
|
|
11610
11853
|
return customName ?? `idx_${table}_${columns.join("_")}`;
|
|
11611
11854
|
}
|
|
@@ -11660,11 +11903,22 @@ function buildPolicySQL(tableName, policy, schemaName) {
|
|
|
11660
11903
|
}
|
|
11661
11904
|
function buildSequenceClause(verb, seqName, seq, includeCycleNoCycle = false) {
|
|
11662
11905
|
const parts = [`${verb} ${seqName}`];
|
|
11663
|
-
if (seq.startWith !== void 0)
|
|
11906
|
+
if (seq.startWith !== void 0)
|
|
11907
|
+
parts.push(
|
|
11908
|
+
`START WITH ${assertNumericLiteral(seq.startWith, "sequence START WITH")}`
|
|
11909
|
+
);
|
|
11664
11910
|
if (seq.incrementBy !== void 0)
|
|
11665
|
-
parts.push(
|
|
11666
|
-
|
|
11667
|
-
|
|
11911
|
+
parts.push(
|
|
11912
|
+
`INCREMENT BY ${assertNumericLiteral(seq.incrementBy, "sequence INCREMENT BY")}`
|
|
11913
|
+
);
|
|
11914
|
+
if (seq.minValue !== void 0)
|
|
11915
|
+
parts.push(
|
|
11916
|
+
`MINVALUE ${assertNumericLiteral(seq.minValue, "sequence MINVALUE")}`
|
|
11917
|
+
);
|
|
11918
|
+
if (seq.maxValue !== void 0)
|
|
11919
|
+
parts.push(
|
|
11920
|
+
`MAXVALUE ${assertNumericLiteral(seq.maxValue, "sequence MAXVALUE")}`
|
|
11921
|
+
);
|
|
11668
11922
|
if (includeCycleNoCycle) {
|
|
11669
11923
|
if (seq.cycle !== void 0) parts.push(seq.cycle ? "CYCLE" : "NO CYCLE");
|
|
11670
11924
|
} else {
|
|
@@ -11674,7 +11928,7 @@ function buildSequenceClause(verb, seqName, seq, includeCycleNoCycle = false) {
|
|
|
11674
11928
|
}
|
|
11675
11929
|
function failSafeUnknownDownChange(kind) {
|
|
11676
11930
|
return {
|
|
11677
|
-
sql: `-- WARNING: Cannot reverse unsupported SchemaChange kind "${kind}"`,
|
|
11931
|
+
sql: `-- WARNING: Cannot reverse unsupported SchemaChange kind "${sanitizeCommentText(kind)}"`,
|
|
11678
11932
|
destructive: true
|
|
11679
11933
|
};
|
|
11680
11934
|
}
|
|
@@ -11736,7 +11990,7 @@ function generateMigrationSQL(diff, options) {
|
|
|
11736
11990
|
[],
|
|
11737
11991
|
// 8: alter column
|
|
11738
11992
|
[],
|
|
11739
|
-
// 9: add PK
|
|
11993
|
+
// 9: add PK / column UNIQUE constraint
|
|
11740
11994
|
[],
|
|
11741
11995
|
// 10: add FK
|
|
11742
11996
|
[],
|
|
@@ -11823,6 +12077,7 @@ function getPhase(kind) {
|
|
|
11823
12077
|
case "alter_column_collation":
|
|
11824
12078
|
case "alter_column_identity":
|
|
11825
12079
|
return 8;
|
|
12080
|
+
case "alter_column_unique":
|
|
11826
12081
|
case "add_primary_key":
|
|
11827
12082
|
return 9;
|
|
11828
12083
|
case "add_foreign_key":
|
|
@@ -11859,7 +12114,7 @@ function upAddColumn(change, schemaName) {
|
|
|
11859
12114
|
}
|
|
11860
12115
|
function upAlterColumnType(change, schemaName) {
|
|
11861
12116
|
const col = change.meta?.column;
|
|
11862
|
-
const toType = col ? mapColumnType(col) :
|
|
12117
|
+
const toType = col ? mapColumnType(col) : validateDbTypeName(change.meta?.toType);
|
|
11863
12118
|
return `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} TYPE ${toType};`;
|
|
11864
12119
|
}
|
|
11865
12120
|
function upAlterColumnNullable(change, schemaName) {
|
|
@@ -11874,6 +12129,34 @@ function upAlterColumnDefault(change, schemaName) {
|
|
|
11874
12129
|
}
|
|
11875
12130
|
return `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} SET DEFAULT ${formatDefault(def)};`;
|
|
11876
12131
|
}
|
|
12132
|
+
function addColumnUniqueSQL(table, column, schemaName, constraintName) {
|
|
12133
|
+
const name = constraintName ?? uniqueName(table, column);
|
|
12134
|
+
if (constraintName !== void 0) validateIdentifier(constraintName, "alias");
|
|
12135
|
+
const quotedConstraintName = quoteIdent2(name, "alias");
|
|
12136
|
+
return `ALTER TABLE ${qualifyTable(table, schemaName)} ADD CONSTRAINT ${quotedConstraintName} UNIQUE (${quoteIdent2(column, "alias")});`;
|
|
12137
|
+
}
|
|
12138
|
+
function dropColumnUniqueSQL(table, column, schemaName, constraintName) {
|
|
12139
|
+
const name = constraintName ?? uniqueName(table, column);
|
|
12140
|
+
if (constraintName !== void 0) validateIdentifier(constraintName, "alias");
|
|
12141
|
+
const quotedConstraintName = quoteIdent2(name, "alias");
|
|
12142
|
+
return `ALTER TABLE ${qualifyTable(table, schemaName)} DROP CONSTRAINT IF EXISTS ${quotedConstraintName};`;
|
|
12143
|
+
}
|
|
12144
|
+
function upAlterColumnUnique(change, schemaName) {
|
|
12145
|
+
const unique = change.meta?.unique;
|
|
12146
|
+
if (unique === void 0 || !change.column) return void 0;
|
|
12147
|
+
const constraintName = change.meta?.constraintName;
|
|
12148
|
+
return unique ? addColumnUniqueSQL(
|
|
12149
|
+
change.table,
|
|
12150
|
+
change.column,
|
|
12151
|
+
schemaName,
|
|
12152
|
+
constraintName
|
|
12153
|
+
) : dropColumnUniqueSQL(
|
|
12154
|
+
change.table,
|
|
12155
|
+
change.column,
|
|
12156
|
+
schemaName,
|
|
12157
|
+
constraintName
|
|
12158
|
+
);
|
|
12159
|
+
}
|
|
11877
12160
|
function upAddPrimaryKey(change, schemaName) {
|
|
11878
12161
|
const columns = change.meta?.columns;
|
|
11879
12162
|
const pkCols = columns.map((n) => quoteIdent2(n, "alias")).join(", ");
|
|
@@ -11902,8 +12185,9 @@ function upCreateIndex(change, schemaName) {
|
|
|
11902
12185
|
"alias"
|
|
11903
12186
|
);
|
|
11904
12187
|
const unique = idx.unique ? "UNIQUE " : "";
|
|
11905
|
-
|
|
11906
|
-
|
|
12188
|
+
const indexMethod = idx.method;
|
|
12189
|
+
if (indexMethod) validateIndexMethod(indexMethod, "index method");
|
|
12190
|
+
const method = indexMethod ? ` USING ${indexMethod}` : "";
|
|
11907
12191
|
const colParts = [
|
|
11908
12192
|
...(idx.expressions ?? []).map((expr) => {
|
|
11909
12193
|
validateSqlExpression(expr, "index expression");
|
|
@@ -11920,10 +12204,11 @@ function upCreateIndex(change, schemaName) {
|
|
|
11920
12204
|
const nullsNotDistinct = idx.unique && idx.nullsNotDistinct ? " NULLS NOT DISTINCT" : "";
|
|
11921
12205
|
const withParams = idx.with && Object.keys(idx.with).length > 0 ? ` WITH (${Object.entries(idx.with).map(([k, v]) => {
|
|
11922
12206
|
validateIdentifier(k, "alias");
|
|
11923
|
-
return `${k} = ${v}`;
|
|
12207
|
+
return `${k} = ${formatStorageParameterValue(v, `index WITH parameter "${k}"`)}`;
|
|
11924
12208
|
}).join(", ")})` : "";
|
|
11925
|
-
|
|
11926
|
-
|
|
12209
|
+
const whereExpr = idx.where;
|
|
12210
|
+
if (whereExpr) validateSqlExpression(whereExpr, "index WHERE predicate");
|
|
12211
|
+
const where = whereExpr ? ` WHERE ${whereExpr}` : "";
|
|
11927
12212
|
return `CREATE ${unique}INDEX IF NOT EXISTS ${indexName} ON ${qualifyTable(change.table, schemaName)}${method} (${cols})${include}${nullsNotDistinct}${withParams}${where};`;
|
|
11928
12213
|
}
|
|
11929
12214
|
function upDropIndex(change, schemaName) {
|
|
@@ -11939,12 +12224,12 @@ function upDropIndex(change, schemaName) {
|
|
|
11939
12224
|
function upAddCheckConstraint(change, schemaName) {
|
|
11940
12225
|
const check = change.meta?.check;
|
|
11941
12226
|
if (!check) return void 0;
|
|
12227
|
+
const expression = check.expression;
|
|
11942
12228
|
const notValid = check.notValid ? " NOT VALID" : "";
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
"
|
|
12229
|
+
validateCheckExpression(expression, "migration check constraint expression");
|
|
12230
|
+
return buildDoBlock(
|
|
12231
|
+
"BEGIN ALTER TABLE " + qualifyTable(change.table, schemaName) + " ADD CONSTRAINT " + quoteIdent2(check.name, "alias") + " " + expression + notValid + "; EXCEPTION WHEN duplicate_object THEN NULL; END"
|
|
11946
12232
|
);
|
|
11947
|
-
return "DO $$ BEGIN ALTER TABLE " + qualifyTable(change.table, schemaName) + " ADD CONSTRAINT " + quoteIdent2(check.name, "alias") + " " + check.expression + notValid + "; EXCEPTION WHEN duplicate_object THEN NULL; END $$;";
|
|
11948
12233
|
}
|
|
11949
12234
|
function upValidateConstraint(change, schemaName) {
|
|
11950
12235
|
const fk = change.meta?.fk;
|
|
@@ -12013,6 +12298,7 @@ function upAlterColumnIdentity(change, schemaName) {
|
|
|
12013
12298
|
function upAddComment(change, schemaName) {
|
|
12014
12299
|
const comment = change.meta?.comment;
|
|
12015
12300
|
const target = change.meta?.target;
|
|
12301
|
+
assertString(comment, "migration comment");
|
|
12016
12302
|
const escaped = comment.replace(/'/g, "''");
|
|
12017
12303
|
if (target === "table") {
|
|
12018
12304
|
return `COMMENT ON TABLE ${qualifyTable(change.table, schemaName)} IS '${escaped}';`;
|
|
@@ -12040,6 +12326,8 @@ function changeToUpSQL(change, schemaName) {
|
|
|
12040
12326
|
return upAlterColumnNullable(change, schemaName);
|
|
12041
12327
|
case "alter_column_default":
|
|
12042
12328
|
return upAlterColumnDefault(change, schemaName);
|
|
12329
|
+
case "alter_column_unique":
|
|
12330
|
+
return upAlterColumnUnique(change, schemaName);
|
|
12043
12331
|
case "add_primary_key":
|
|
12044
12332
|
return upAddPrimaryKey(change, schemaName);
|
|
12045
12333
|
case "drop_primary_key":
|
|
@@ -12137,7 +12425,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12137
12425
|
};
|
|
12138
12426
|
case "drop_table":
|
|
12139
12427
|
return {
|
|
12140
|
-
sql: `-- WARNING: Cannot reverse drop_table "${change.table}" -- table data was lost`,
|
|
12428
|
+
sql: `-- WARNING: Cannot reverse drop_table "${sanitizeCommentText(change.table)}" -- table data was lost`,
|
|
12141
12429
|
destructive: true
|
|
12142
12430
|
};
|
|
12143
12431
|
case "add_column":
|
|
@@ -12147,19 +12435,20 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12147
12435
|
};
|
|
12148
12436
|
case "drop_column":
|
|
12149
12437
|
return {
|
|
12150
|
-
sql: `-- WARNING: Cannot reverse drop_column "${change.table}"."${change.column}" -- column data was lost`,
|
|
12438
|
+
sql: `-- WARNING: Cannot reverse drop_column "${sanitizeCommentText(change.table)}"."${sanitizeCommentText(change.column)}" -- column data was lost`,
|
|
12151
12439
|
destructive: true
|
|
12152
12440
|
};
|
|
12153
12441
|
case "alter_column_type": {
|
|
12154
12442
|
const fromType = change.meta?.fromType;
|
|
12155
|
-
if (
|
|
12443
|
+
if (fromType == null) {
|
|
12156
12444
|
return {
|
|
12157
|
-
sql: `-- WARNING: Cannot reverse alter_column_type "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
12445
|
+
sql: `-- WARNING: Cannot reverse alter_column_type "${sanitizeCommentText(change.table)}"."${sanitizeCommentText(change.column)}" -- missing migration metadata`,
|
|
12158
12446
|
destructive: true
|
|
12159
12447
|
};
|
|
12160
12448
|
}
|
|
12449
|
+
const safeFromType = validateDbTypeName(fromType);
|
|
12161
12450
|
return {
|
|
12162
|
-
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} TYPE ${
|
|
12451
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} TYPE ${safeFromType};`,
|
|
12163
12452
|
destructive: true
|
|
12164
12453
|
};
|
|
12165
12454
|
}
|
|
@@ -12167,7 +12456,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12167
12456
|
const oldNullable = change.meta?.oldNullable;
|
|
12168
12457
|
if (oldNullable === void 0) {
|
|
12169
12458
|
return {
|
|
12170
|
-
sql: `-- WARNING: Cannot reverse alter_column_nullable "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
12459
|
+
sql: `-- WARNING: Cannot reverse alter_column_nullable "${sanitizeCommentText(change.table)}"."${sanitizeCommentText(change.column)}" -- missing migration metadata`,
|
|
12171
12460
|
destructive: true
|
|
12172
12461
|
};
|
|
12173
12462
|
}
|
|
@@ -12182,7 +12471,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12182
12471
|
const oldDefault = change.meta?.oldDefault;
|
|
12183
12472
|
if (oldDefault === void 0) {
|
|
12184
12473
|
return {
|
|
12185
|
-
sql: `-- WARNING: Cannot reverse alter_column_default "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
12474
|
+
sql: `-- WARNING: Cannot reverse alter_column_default "${sanitizeCommentText(change.table)}"."${sanitizeCommentText(change.column)}" -- missing migration metadata`,
|
|
12186
12475
|
destructive: true
|
|
12187
12476
|
};
|
|
12188
12477
|
}
|
|
@@ -12199,6 +12488,30 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12199
12488
|
destructive: false
|
|
12200
12489
|
};
|
|
12201
12490
|
}
|
|
12491
|
+
case "alter_column_unique": {
|
|
12492
|
+
const unique = change.meta?.unique;
|
|
12493
|
+
if (unique === void 0 || !change.column) {
|
|
12494
|
+
return {
|
|
12495
|
+
sql: `-- WARNING: Cannot reverse alter_column_unique "${sanitizeCommentText(change.table)}"."${sanitizeCommentText(change.column)}" -- missing migration metadata`,
|
|
12496
|
+
destructive: true
|
|
12497
|
+
};
|
|
12498
|
+
}
|
|
12499
|
+
const constraintName = change.meta?.constraintName;
|
|
12500
|
+
return {
|
|
12501
|
+
sql: unique ? dropColumnUniqueSQL(
|
|
12502
|
+
change.table,
|
|
12503
|
+
change.column,
|
|
12504
|
+
schemaName,
|
|
12505
|
+
constraintName
|
|
12506
|
+
) : addColumnUniqueSQL(
|
|
12507
|
+
change.table,
|
|
12508
|
+
change.column,
|
|
12509
|
+
schemaName,
|
|
12510
|
+
constraintName
|
|
12511
|
+
),
|
|
12512
|
+
destructive: unique === true
|
|
12513
|
+
};
|
|
12514
|
+
}
|
|
12202
12515
|
case "add_primary_key": {
|
|
12203
12516
|
return {
|
|
12204
12517
|
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP CONSTRAINT IF EXISTS ${quoteIdent2(pkName(change.table), "alias")} CASCADE;`,
|
|
@@ -12216,7 +12529,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12216
12529
|
};
|
|
12217
12530
|
}
|
|
12218
12531
|
return {
|
|
12219
|
-
sql: `-- WARNING: Cannot reverse drop_primary_key "${change.table}" -- columns unknown`,
|
|
12532
|
+
sql: `-- WARNING: Cannot reverse drop_primary_key "${sanitizeCommentText(change.table)}" -- columns unknown`,
|
|
12220
12533
|
destructive: true
|
|
12221
12534
|
};
|
|
12222
12535
|
}
|
|
@@ -12236,7 +12549,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12236
12549
|
const fk = change.meta?.fk;
|
|
12237
12550
|
if (!fk) {
|
|
12238
12551
|
return {
|
|
12239
|
-
sql: `-- WARNING: Cannot reverse drop_foreign_key "${change.table}" -- FK definition was lost`,
|
|
12552
|
+
sql: `-- WARNING: Cannot reverse drop_foreign_key "${sanitizeCommentText(change.table)}" -- FK definition was lost`,
|
|
12240
12553
|
destructive: true
|
|
12241
12554
|
};
|
|
12242
12555
|
}
|
|
@@ -12251,7 +12564,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12251
12564
|
const fk = change.meta?.fk;
|
|
12252
12565
|
if (!oldFk || !fk) {
|
|
12253
12566
|
return {
|
|
12254
|
-
sql: `-- WARNING: Cannot reverse alter_foreign_key "${change.table}" -- missing migration metadata`,
|
|
12567
|
+
sql: `-- WARNING: Cannot reverse alter_foreign_key "${sanitizeCommentText(change.table)}" -- missing migration metadata`,
|
|
12255
12568
|
destructive: true
|
|
12256
12569
|
};
|
|
12257
12570
|
}
|
|
@@ -12281,7 +12594,7 @@ ${add}`, destructive: false };
|
|
|
12281
12594
|
const idx = change.meta?.index;
|
|
12282
12595
|
if (!idx) {
|
|
12283
12596
|
return {
|
|
12284
|
-
sql: `-- WARNING: Cannot reverse drop_index "${change.table}" -- index definition was lost`,
|
|
12597
|
+
sql: `-- WARNING: Cannot reverse drop_index "${sanitizeCommentText(change.table)}" -- index definition was lost`,
|
|
12285
12598
|
destructive: true
|
|
12286
12599
|
};
|
|
12287
12600
|
}
|
|
@@ -12302,12 +12615,12 @@ ${add}`, destructive: false };
|
|
|
12302
12615
|
case "drop_check_constraint": {
|
|
12303
12616
|
const check = change.meta?.check;
|
|
12304
12617
|
if (!check) return { sql: void 0, destructive: true };
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
"migration check constraint (down)"
|
|
12308
|
-
);
|
|
12618
|
+
const expression = check.expression;
|
|
12619
|
+
validateCheckExpression(expression, "migration check constraint (down)");
|
|
12309
12620
|
return {
|
|
12310
|
-
sql:
|
|
12621
|
+
sql: buildDoBlock(
|
|
12622
|
+
"BEGIN ALTER TABLE " + qualifyTable(change.table, schemaName) + " ADD CONSTRAINT " + quoteIdent2(check.name, "alias") + " " + expression + "; EXCEPTION WHEN duplicate_object THEN NULL; END"
|
|
12623
|
+
),
|
|
12311
12624
|
// Allowlisted: re-adds the dropped CHECK constraint from metadata.
|
|
12312
12625
|
destructive: false
|
|
12313
12626
|
};
|
|
@@ -12325,7 +12638,10 @@ ${add}`, destructive: false };
|
|
|
12325
12638
|
const enumDef = change.meta?.enum;
|
|
12326
12639
|
if (!enumDef) return { sql: void 0, destructive: true };
|
|
12327
12640
|
const enumName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(enumDef.name, "alias")}` : quoteIdent2(enumDef.name, "alias");
|
|
12328
|
-
const values = enumDef.values.map((v) =>
|
|
12641
|
+
const values = enumDef.values.map((v) => {
|
|
12642
|
+
validateEnumLabel(v, "enum value");
|
|
12643
|
+
return `'${v.replace(/'/g, "''")}'`;
|
|
12644
|
+
}).join(", ");
|
|
12329
12645
|
return {
|
|
12330
12646
|
sql: `CREATE TYPE ${enumName} AS ENUM (${values});`,
|
|
12331
12647
|
// Allowlisted: re-creates the dropped enum type from metadata.
|
|
@@ -12339,7 +12655,7 @@ ${add}`, destructive: false };
|
|
|
12339
12655
|
};
|
|
12340
12656
|
case "alter_column_collation": {
|
|
12341
12657
|
return {
|
|
12342
|
-
sql: `-- WARNING: Cannot reverse alter_column_collation "${change.table}"."${change.column}" -- previous collation unknown`,
|
|
12658
|
+
sql: `-- WARNING: Cannot reverse alter_column_collation "${sanitizeCommentText(change.table)}"."${sanitizeCommentText(change.column)}" -- previous collation unknown`,
|
|
12343
12659
|
destructive: true
|
|
12344
12660
|
};
|
|
12345
12661
|
}
|
|
@@ -12365,7 +12681,7 @@ ${add}`, destructive: false };
|
|
|
12365
12681
|
}
|
|
12366
12682
|
if (!prevIdentity && !col.identity) {
|
|
12367
12683
|
return {
|
|
12368
|
-
sql: `-- WARNING: Cannot reverse alter_column_identity "${change.table}"."${change.column}" -- missing previous identity metadata`,
|
|
12684
|
+
sql: `-- WARNING: Cannot reverse alter_column_identity "${sanitizeCommentText(change.table)}"."${sanitizeCommentText(change.column)}" -- missing previous identity metadata`,
|
|
12369
12685
|
destructive: true
|
|
12370
12686
|
};
|
|
12371
12687
|
}
|
|
@@ -12392,7 +12708,8 @@ ${add}`, destructive: false };
|
|
|
12392
12708
|
case "drop_comment": {
|
|
12393
12709
|
const target = change.meta?.target;
|
|
12394
12710
|
const comment = change.meta?.comment;
|
|
12395
|
-
if (
|
|
12711
|
+
if (comment !== void 0) {
|
|
12712
|
+
assertString(comment, "migration comment");
|
|
12396
12713
|
const escaped = comment.replace(/'/g, "''");
|
|
12397
12714
|
if (target === "table") {
|
|
12398
12715
|
return {
|
|
@@ -12409,8 +12726,9 @@ ${add}`, destructive: false };
|
|
|
12409
12726
|
};
|
|
12410
12727
|
}
|
|
12411
12728
|
}
|
|
12729
|
+
const columnTarget2 = change.column === void 0 ? "" : `."${sanitizeCommentText(change.column)}"`;
|
|
12412
12730
|
return {
|
|
12413
|
-
sql:
|
|
12731
|
+
sql: '-- WARNING: Cannot reverse drop_comment "' + sanitizeCommentText(change.table) + '"' + columnTarget2 + " -- comment text was lost",
|
|
12414
12732
|
destructive: true
|
|
12415
12733
|
};
|
|
12416
12734
|
}
|
|
@@ -12444,7 +12762,7 @@ ${add}`, destructive: false };
|
|
|
12444
12762
|
const prevSeq = change.meta?.previousSequence;
|
|
12445
12763
|
if (!prevSeq) {
|
|
12446
12764
|
return {
|
|
12447
|
-
sql: `-- WARNING: Cannot reverse alter_sequence "${change.table}" -- missing migration metadata`,
|
|
12765
|
+
sql: `-- WARNING: Cannot reverse alter_sequence "${sanitizeCommentText(change.table)}" -- missing migration metadata`,
|
|
12448
12766
|
destructive: true
|
|
12449
12767
|
};
|
|
12450
12768
|
}
|
|
@@ -12467,7 +12785,7 @@ ${add}`, destructive: false };
|
|
|
12467
12785
|
}
|
|
12468
12786
|
case "validate_constraint":
|
|
12469
12787
|
return {
|
|
12470
|
-
sql: `-- VALIDATE CONSTRAINT cannot be reversed in PostgreSQL (table: "${change.table}")`,
|
|
12788
|
+
sql: `-- VALIDATE CONSTRAINT cannot be reversed in PostgreSQL (table: "${sanitizeCommentText(change.table)}")`,
|
|
12471
12789
|
destructive: true
|
|
12472
12790
|
};
|
|
12473
12791
|
case "enable_rls":
|
|
@@ -12508,7 +12826,6 @@ function generateDownSQL(diff, options) {
|
|
|
12508
12826
|
function generateDownMigrationSQL(diff, options) {
|
|
12509
12827
|
const schemaName = options?.schemaName;
|
|
12510
12828
|
const includeDestructive = options?.includeDestructive ?? true;
|
|
12511
|
-
const changes = includeDestructive ? diff.changes : diff.changes.filter((c) => !c.destructive);
|
|
12512
12829
|
const phases = [
|
|
12513
12830
|
[],
|
|
12514
12831
|
// 0: drop FK, drop CHECK
|
|
@@ -12529,7 +12846,7 @@ function generateDownMigrationSQL(diff, options) {
|
|
|
12529
12846
|
[],
|
|
12530
12847
|
// 8: alter column
|
|
12531
12848
|
[],
|
|
12532
|
-
// 9: add PK
|
|
12849
|
+
// 9: add PK / column UNIQUE constraint
|
|
12533
12850
|
[],
|
|
12534
12851
|
// 10: add FK
|
|
12535
12852
|
[],
|
|
@@ -12549,7 +12866,7 @@ function generateDownMigrationSQL(diff, options) {
|
|
|
12549
12866
|
[]
|
|
12550
12867
|
// 18: CREATE/DROP POLICY
|
|
12551
12868
|
];
|
|
12552
|
-
for (const change of changes) {
|
|
12869
|
+
for (const change of diff.changes) {
|
|
12553
12870
|
const phase = getPhase(change.kind);
|
|
12554
12871
|
phases[phase].push(change);
|
|
12555
12872
|
}
|
|
@@ -12558,6 +12875,7 @@ function generateDownMigrationSQL(diff, options) {
|
|
|
12558
12875
|
for (let i = phases.length - 1; i >= 0; i--) {
|
|
12559
12876
|
for (const change of phases[i]) {
|
|
12560
12877
|
const down = changeToDownSQL(change, schemaName);
|
|
12878
|
+
if (!includeDestructive && down.destructive) continue;
|
|
12561
12879
|
destructive = down.destructive || destructive;
|
|
12562
12880
|
if (down.sql) statements.push(down.sql);
|
|
12563
12881
|
}
|
|
@@ -12602,7 +12920,10 @@ function generateAddFKSQL(tableName, fk, schemaName) {
|
|
|
12602
12920
|
const qualTable = qualifyTable(tableName, schemaName);
|
|
12603
12921
|
const constraintName = quoteIdent2(fkName(tableName, fk.columns), "alias");
|
|
12604
12922
|
const fkCols = fk.columns.map((n) => quoteIdent2(n, "alias")).join(", ");
|
|
12605
|
-
const refTable = qualifyTable(
|
|
12923
|
+
const refTable = qualifyTable(
|
|
12924
|
+
fk.references.table,
|
|
12925
|
+
fk.references.schema ?? schemaName
|
|
12926
|
+
);
|
|
12606
12927
|
const refCols = fk.references.columns.map((n) => quoteIdent2(n, "alias")).join(", ");
|
|
12607
12928
|
const onDelete = fk.onDelete ? ` ON DELETE ${mapOnDeleteAction(fk.onDelete)}` : "";
|
|
12608
12929
|
const onUpdate = fk.onUpdate ? ` ON UPDATE ${mapOnDeleteAction(fk.onUpdate)}` : "";
|
|
@@ -12688,10 +13009,11 @@ function quoteIdentifier(name) {
|
|
|
12688
13009
|
validateIdentifier(name, "alias");
|
|
12689
13010
|
return `"${name.replace(/"/g, '""')}"`;
|
|
12690
13011
|
}
|
|
12691
|
-
function qualifyTable2(tableName, schemaName, naming) {
|
|
13012
|
+
function qualifyTable2(tableName, schemaName, naming, transformSchema = true) {
|
|
12692
13013
|
const table = quoteIdentifier(naming.toDatabase(tableName));
|
|
12693
13014
|
if (schemaName) {
|
|
12694
|
-
|
|
13015
|
+
const schema = transformSchema ? naming.toDatabase(schemaName) : schemaName;
|
|
13016
|
+
return `${quoteIdentifier(schema)}.${table}`;
|
|
12695
13017
|
}
|
|
12696
13018
|
return table;
|
|
12697
13019
|
}
|
|
@@ -12762,7 +13084,7 @@ function generateAlterTableAddFK(tableName, fk, schemaName, naming) {
|
|
|
12762
13084
|
`fk_${tableName}_${fk.columns.join("_")}`
|
|
12763
13085
|
);
|
|
12764
13086
|
const fkCols = fk.columns.map((col) => quoteIdentifier(naming.toDatabase(col))).join(", ");
|
|
12765
|
-
const refTable = qualifyTable2(fk.references.table, schemaName, naming);
|
|
13087
|
+
const refTable = fk.references.schema !== void 0 ? qualifyTable2(fk.references.table, fk.references.schema, naming, false) : qualifyTable2(fk.references.table, schemaName, naming);
|
|
12766
13088
|
const refCols = fk.references.columns.map((col) => quoteIdentifier(naming.toDatabase(col))).join(", ");
|
|
12767
13089
|
const onDelete = fk.onDelete ? ` ON DELETE ${mapOnDeleteAction(fk.onDelete)}` : "";
|
|
12768
13090
|
const onUpdate = fk.onUpdate ? ` ON UPDATE ${mapOnDeleteAction(fk.onUpdate)}` : "";
|
|
@@ -12776,8 +13098,9 @@ function generateCreateIndex(tableName, idx, schemaName, naming) {
|
|
|
12776
13098
|
);
|
|
12777
13099
|
const qualifiedTable = qualifyTable2(tableName, schemaName, naming);
|
|
12778
13100
|
const unique = idx.unique ? "UNIQUE " : "";
|
|
12779
|
-
|
|
12780
|
-
|
|
13101
|
+
const indexMethod = idx.method;
|
|
13102
|
+
if (indexMethod) validateIndexMethod(indexMethod, "index method");
|
|
13103
|
+
const method = indexMethod ? ` USING ${indexMethod}` : "";
|
|
12781
13104
|
const colParts = [];
|
|
12782
13105
|
if (idx.expressions && idx.expressions.length > 0) {
|
|
12783
13106
|
for (const expr of idx.expressions) {
|
|
@@ -12797,10 +13120,11 @@ function generateCreateIndex(tableName, idx, schemaName, naming) {
|
|
|
12797
13120
|
const nullsNotDistinct = idx.unique && idx.nullsNotDistinct ? " NULLS NOT DISTINCT" : "";
|
|
12798
13121
|
const withParams = idx.with && Object.keys(idx.with).length > 0 ? ` WITH (${Object.entries(idx.with).map(([k, v]) => {
|
|
12799
13122
|
validateIdentifier(k, "alias");
|
|
12800
|
-
return `${k} = ${v}`;
|
|
13123
|
+
return `${k} = ${formatStorageParameterValue(v, `index WITH parameter "${k}"`)}`;
|
|
12801
13124
|
}).join(", ")})` : "";
|
|
12802
|
-
|
|
12803
|
-
|
|
13125
|
+
const whereExpr = idx.where;
|
|
13126
|
+
if (whereExpr) validateSqlExpression(whereExpr, "index WHERE predicate");
|
|
13127
|
+
const where = whereExpr ? ` WHERE ${whereExpr}` : "";
|
|
12804
13128
|
return `CREATE ${unique}INDEX ${indexName} ON ${qualifiedTable}${method} (${cols})${include}${nullsNotDistinct}${withParams}${where};`;
|
|
12805
13129
|
}
|
|
12806
13130
|
function generateCreatePolicy(tableName, policy, schemaName, naming) {
|
|
@@ -13167,7 +13491,8 @@ function normalizeTable(table, plugin) {
|
|
|
13167
13491
|
columns: fk.columns.map(toDb),
|
|
13168
13492
|
references: {
|
|
13169
13493
|
table: toDb(fk.references.table),
|
|
13170
|
-
columns: fk.references.columns.map(toDb)
|
|
13494
|
+
columns: fk.references.columns.map(toDb),
|
|
13495
|
+
...fk.references.schema !== void 0 ? { schema: fk.references.schema } : {}
|
|
13171
13496
|
}
|
|
13172
13497
|
})),
|
|
13173
13498
|
indexes: table.indexes.map((idx) => ({
|
|
@@ -13260,6 +13585,21 @@ function compareColumnDetails(tableName, schema, db, changes) {
|
|
|
13260
13585
|
meta: { default: schema.default, oldDefault: db.default }
|
|
13261
13586
|
});
|
|
13262
13587
|
}
|
|
13588
|
+
const schemaUnique = schema.unique === true;
|
|
13589
|
+
const dbUnique = db.unique === true;
|
|
13590
|
+
if (schemaUnique !== dbUnique) {
|
|
13591
|
+
changes.push({
|
|
13592
|
+
kind: "alter_column_unique",
|
|
13593
|
+
table: tableName,
|
|
13594
|
+
column: schema.name,
|
|
13595
|
+
destructive: schemaUnique === false,
|
|
13596
|
+
details: `Change unique of "${schema.name}" from ${dbUnique} to ${schemaUnique}`,
|
|
13597
|
+
meta: {
|
|
13598
|
+
unique: schemaUnique,
|
|
13599
|
+
...db.uniqueConstraintName !== void 0 ? { constraintName: db.uniqueConstraintName } : {}
|
|
13600
|
+
}
|
|
13601
|
+
});
|
|
13602
|
+
}
|
|
13263
13603
|
if ((schema.collation ?? null) !== (db.collation ?? null)) {
|
|
13264
13604
|
changes.push({
|
|
13265
13605
|
kind: "alter_column_collation",
|
|
@@ -13331,7 +13671,8 @@ function comparePrimaryKeys(schema, db, changes) {
|
|
|
13331
13671
|
kind: "drop_primary_key",
|
|
13332
13672
|
table: schema.name,
|
|
13333
13673
|
destructive: true,
|
|
13334
|
-
details: `Drop primary key (${dbPK.join(", ")})
|
|
13674
|
+
details: `Drop primary key (${dbPK.join(", ")})`,
|
|
13675
|
+
meta: { columns: dbPK }
|
|
13335
13676
|
});
|
|
13336
13677
|
return;
|
|
13337
13678
|
}
|
|
@@ -13339,7 +13680,8 @@ function comparePrimaryKeys(schema, db, changes) {
|
|
|
13339
13680
|
kind: "drop_primary_key",
|
|
13340
13681
|
table: schema.name,
|
|
13341
13682
|
destructive: true,
|
|
13342
|
-
details: `Drop primary key (${dbPK.join(", ")})
|
|
13683
|
+
details: `Drop primary key (${dbPK.join(", ")})`,
|
|
13684
|
+
meta: { columns: dbPK }
|
|
13343
13685
|
});
|
|
13344
13686
|
changes.push({
|
|
13345
13687
|
kind: "add_primary_key",
|
|
@@ -13411,7 +13753,7 @@ function compareForeignKeys(schema, db, changes) {
|
|
|
13411
13753
|
}
|
|
13412
13754
|
}
|
|
13413
13755
|
function fkKey(fk) {
|
|
13414
|
-
return `${fk.columns.join(",")}\u2192${fk.references.table}(${fk.references.columns.join(",")})`;
|
|
13756
|
+
return `${fk.columns.join(",")}\u2192${fk.references.schema ?? ""}.${fk.references.table}(${fk.references.columns.join(",")})`;
|
|
13415
13757
|
}
|
|
13416
13758
|
function compareIndexes(schema, db, changes) {
|
|
13417
13759
|
const explicitIndexCols = new Set(
|
|
@@ -13444,16 +13786,15 @@ function compareIndexes(schema, db, changes) {
|
|
|
13444
13786
|
});
|
|
13445
13787
|
}
|
|
13446
13788
|
}
|
|
13447
|
-
const
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
)
|
|
13789
|
+
const dbColMap = new Map(db.columns.map((col) => [col.name, col]));
|
|
13790
|
+
const autoUniqueIndexColumns = new Set(
|
|
13791
|
+
schema.columns.filter((schemaCol) => {
|
|
13792
|
+
const dbCol = dbColMap.get(schemaCol.name);
|
|
13793
|
+
return dbCol !== void 0 && (schemaCol.unique === true || dbCol.unique === true);
|
|
13794
|
+
}).map((col) => col.name)
|
|
13454
13795
|
);
|
|
13455
13796
|
for (const [key, idx] of dbIdxMap) {
|
|
13456
|
-
if (!schemaIdxMap.has(key) && !autoFkIndexKeys.has(key) && !
|
|
13797
|
+
if (!schemaIdxMap.has(key) && !autoFkIndexKeys.has(key) && !isAutoUniqueIndex(schema.name, idx, autoUniqueIndexColumns)) {
|
|
13457
13798
|
changes.push({
|
|
13458
13799
|
kind: "drop_index",
|
|
13459
13800
|
table: schema.name,
|
|
@@ -13464,6 +13805,16 @@ function compareIndexes(schema, db, changes) {
|
|
|
13464
13805
|
}
|
|
13465
13806
|
}
|
|
13466
13807
|
}
|
|
13808
|
+
function isAutoUniqueIndex(tableName, idx, autoUniqueIndexColumns) {
|
|
13809
|
+
if (idx.columns.length !== 1) return false;
|
|
13810
|
+
const column = idx.columns[0];
|
|
13811
|
+
if (column === void 0 || !autoUniqueIndexColumns.has(column)) return false;
|
|
13812
|
+
if (idx.unique !== true) return false;
|
|
13813
|
+
if (idx.name !== void 0 && idx.name !== `${tableName}_${column}_key`) {
|
|
13814
|
+
return false;
|
|
13815
|
+
}
|
|
13816
|
+
return idx.nullsNotDistinct !== true && (idx.method === void 0 || idx.method === "btree") && idx.where === void 0 && (idx.expressions === void 0 || idx.expressions.length === 0) && (idx.include === void 0 || idx.include.length === 0) && (idx.opclass === void 0 || Object.keys(idx.opclass).length === 0) && (idx.with === void 0 || Object.keys(idx.with).length === 0);
|
|
13817
|
+
}
|
|
13467
13818
|
function indexKey(idx) {
|
|
13468
13819
|
const parts = [
|
|
13469
13820
|
idx.columns.join(","),
|
|
@@ -13828,6 +14179,7 @@ function buildSummary(changes) {
|
|
|
13828
14179
|
case "alter_column_type":
|
|
13829
14180
|
case "alter_column_nullable":
|
|
13830
14181
|
case "alter_column_default":
|
|
14182
|
+
case "alter_column_unique":
|
|
13831
14183
|
columns.altered++;
|
|
13832
14184
|
break;
|
|
13833
14185
|
case "add_primary_key":
|
|
@@ -14078,6 +14430,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14078
14430
|
pksResult,
|
|
14079
14431
|
fksResult,
|
|
14080
14432
|
indexesResult,
|
|
14433
|
+
uniqueColumnsResult,
|
|
14081
14434
|
enumsResult,
|
|
14082
14435
|
commentsResult,
|
|
14083
14436
|
checksResult,
|
|
@@ -14111,28 +14464,44 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14111
14464
|
// 3. Foreign keys
|
|
14112
14465
|
pool.query(
|
|
14113
14466
|
`SELECT
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14467
|
+
c.conname AS constraint_name,
|
|
14468
|
+
source_rel.relname AS source_table,
|
|
14469
|
+
source_attr.attname AS source_column,
|
|
14470
|
+
target_ns.nspname AS target_schema,
|
|
14471
|
+
target_rel.relname AS target_table,
|
|
14472
|
+
target_attr.attname AS target_column,
|
|
14473
|
+
CASE c.confdeltype
|
|
14474
|
+
WHEN 'c' THEN 'CASCADE'
|
|
14475
|
+
WHEN 'n' THEN 'SET NULL'
|
|
14476
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
14477
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
14478
|
+
ELSE 'NO ACTION'
|
|
14479
|
+
END AS delete_rule,
|
|
14480
|
+
CASE c.confupdtype
|
|
14481
|
+
WHEN 'c' THEN 'CASCADE'
|
|
14482
|
+
WHEN 'n' THEN 'SET NULL'
|
|
14483
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
14484
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
14485
|
+
ELSE 'NO ACTION'
|
|
14486
|
+
END AS update_rule,
|
|
14487
|
+
CASE WHEN c.condeferrable THEN 'YES' ELSE 'NO' END AS is_deferrable,
|
|
14488
|
+
CASE WHEN c.condeferred THEN 'YES' ELSE 'NO' END AS initially_deferred
|
|
14489
|
+
FROM pg_constraint c
|
|
14490
|
+
JOIN pg_class source_rel ON source_rel.oid = c.conrelid
|
|
14491
|
+
JOIN pg_namespace source_ns ON source_ns.oid = source_rel.relnamespace
|
|
14492
|
+
JOIN pg_class target_rel ON target_rel.oid = c.confrelid
|
|
14493
|
+
JOIN pg_namespace target_ns ON target_ns.oid = target_rel.relnamespace
|
|
14494
|
+
JOIN LATERAL unnest(c.conkey, c.confkey) WITH ORDINALITY AS cols(source_attnum, target_attnum, ord) ON true
|
|
14495
|
+
JOIN pg_attribute source_attr
|
|
14496
|
+
ON source_attr.attrelid = c.conrelid
|
|
14497
|
+
AND source_attr.attnum = cols.source_attnum
|
|
14498
|
+
JOIN pg_attribute target_attr
|
|
14499
|
+
ON target_attr.attrelid = c.confrelid
|
|
14500
|
+
AND target_attr.attnum = cols.target_attnum
|
|
14501
|
+
WHERE c.contype = 'f'
|
|
14502
|
+
AND c.conparentid = 0
|
|
14503
|
+
AND source_ns.nspname = $1
|
|
14504
|
+
ORDER BY c.conname, cols.ord`,
|
|
14136
14505
|
[schema]
|
|
14137
14506
|
),
|
|
14138
14507
|
// 4. Indexes (excluding PK-backing indexes and unique-constraint-backing indexes).
|
|
@@ -14196,7 +14565,19 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14196
14565
|
ORDER BY t.relname, i.relname`,
|
|
14197
14566
|
[schema]
|
|
14198
14567
|
),
|
|
14199
|
-
// 5.
|
|
14568
|
+
// 5. Single-column UNIQUE constraints tracked as ColumnIR.unique
|
|
14569
|
+
pool.query(
|
|
14570
|
+
`SELECT rel.relname AS table_name, att.attname AS column_name, c.conname AS constraint_name
|
|
14571
|
+
FROM pg_constraint c
|
|
14572
|
+
JOIN pg_class rel ON rel.oid = c.conrelid
|
|
14573
|
+
JOIN pg_namespace ns ON ns.oid = rel.relnamespace
|
|
14574
|
+
JOIN pg_attribute att ON att.attrelid = c.conrelid AND att.attnum = c.conkey[1]
|
|
14575
|
+
WHERE c.contype = 'u'
|
|
14576
|
+
AND array_length(c.conkey, 1) = 1
|
|
14577
|
+
AND ns.nspname = $1`,
|
|
14578
|
+
[schema]
|
|
14579
|
+
),
|
|
14580
|
+
// 6. ENUM types
|
|
14200
14581
|
pool.query(
|
|
14201
14582
|
`SELECT
|
|
14202
14583
|
t.typname AS name,
|
|
@@ -14209,7 +14590,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14209
14590
|
GROUP BY t.typname`,
|
|
14210
14591
|
[schema]
|
|
14211
14592
|
),
|
|
14212
|
-
//
|
|
14593
|
+
// 7. Comments (table and column level) from pg_description
|
|
14213
14594
|
pool.query(
|
|
14214
14595
|
`SELECT
|
|
14215
14596
|
c.relname AS table_name,
|
|
@@ -14223,7 +14604,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14223
14604
|
AND d.objsubid >= 0`,
|
|
14224
14605
|
[schema]
|
|
14225
14606
|
),
|
|
14226
|
-
//
|
|
14607
|
+
// 8. CHECK constraints
|
|
14227
14608
|
pool.query(
|
|
14228
14609
|
`SELECT
|
|
14229
14610
|
c.conname AS name,
|
|
@@ -14235,7 +14616,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14235
14616
|
AND n.nspname = $1`,
|
|
14236
14617
|
[schema]
|
|
14237
14618
|
),
|
|
14238
|
-
//
|
|
14619
|
+
// 9. Partition configurations
|
|
14239
14620
|
pool.query(
|
|
14240
14621
|
`SELECT
|
|
14241
14622
|
c.relname AS table_name,
|
|
@@ -14250,13 +14631,13 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14250
14631
|
GROUP BY c.relname, p.partstrat`,
|
|
14251
14632
|
[schema]
|
|
14252
14633
|
),
|
|
14253
|
-
//
|
|
14634
|
+
// 10. Installed extensions (no schema param — queries globally; skip plpgsql)
|
|
14254
14635
|
pool.query(
|
|
14255
14636
|
`SELECT extname AS name
|
|
14256
14637
|
FROM pg_extension
|
|
14257
14638
|
WHERE extname != 'plpgsql'`
|
|
14258
14639
|
),
|
|
14259
|
-
//
|
|
14640
|
+
// 11. Sequences not backed by SERIAL
|
|
14260
14641
|
pool.query(
|
|
14261
14642
|
`SELECT s.sequencename AS name, s.start_value, s.increment_by, s.min_value, s.max_value, s.cycle
|
|
14262
14643
|
FROM pg_sequences s
|
|
@@ -14267,7 +14648,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14267
14648
|
AND d.objid IS NULL`,
|
|
14268
14649
|
[schema]
|
|
14269
14650
|
),
|
|
14270
|
-
//
|
|
14651
|
+
// 12. RLS enabled state per table
|
|
14271
14652
|
pool.query(
|
|
14272
14653
|
`SELECT c.relname AS table_name, c.relrowsecurity AS rls_enabled
|
|
14273
14654
|
FROM pg_class c
|
|
@@ -14276,7 +14657,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14276
14657
|
AND c.relkind = 'r'`,
|
|
14277
14658
|
[schema]
|
|
14278
14659
|
),
|
|
14279
|
-
//
|
|
14660
|
+
// 13. Row-Level Security policies
|
|
14280
14661
|
pool.query(
|
|
14281
14662
|
`SELECT
|
|
14282
14663
|
c.relname AS table_name,
|
|
@@ -14298,6 +14679,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14298
14679
|
pks: pksResult.rows,
|
|
14299
14680
|
fks: fksResult.rows,
|
|
14300
14681
|
indexes: indexesResult.rows,
|
|
14682
|
+
uniqueColumns: uniqueColumnsResult.rows,
|
|
14301
14683
|
enums: enumsResult.rows,
|
|
14302
14684
|
comments: commentsResult.rows,
|
|
14303
14685
|
checks: checksResult.rows,
|
|
@@ -14383,6 +14765,21 @@ function buildIndexMap(rows) {
|
|
|
14383
14765
|
}
|
|
14384
14766
|
return result;
|
|
14385
14767
|
}
|
|
14768
|
+
function buildUniqueColumnMap(rows) {
|
|
14769
|
+
const result = /* @__PURE__ */ new Map();
|
|
14770
|
+
for (const row of rows) {
|
|
14771
|
+
const existing = result.get(row.table_name);
|
|
14772
|
+
if (existing) {
|
|
14773
|
+
existing.set(row.column_name, row.constraint_name);
|
|
14774
|
+
} else {
|
|
14775
|
+
result.set(
|
|
14776
|
+
row.table_name,
|
|
14777
|
+
/* @__PURE__ */ new Map([[row.column_name, row.constraint_name]])
|
|
14778
|
+
);
|
|
14779
|
+
}
|
|
14780
|
+
}
|
|
14781
|
+
return result;
|
|
14782
|
+
}
|
|
14386
14783
|
function buildColumnMap(rows) {
|
|
14387
14784
|
const result = /* @__PURE__ */ new Map();
|
|
14388
14785
|
for (const col of rows) {
|
|
@@ -14410,14 +14807,16 @@ function buildPKMap(rows) {
|
|
|
14410
14807
|
function buildFKMap(rows) {
|
|
14411
14808
|
const result = /* @__PURE__ */ new Map();
|
|
14412
14809
|
for (const fk of rows) {
|
|
14413
|
-
const
|
|
14810
|
+
const key = JSON.stringify([fk.source_table, fk.constraint_name]);
|
|
14811
|
+
const existing = result.get(key);
|
|
14414
14812
|
if (existing) {
|
|
14415
14813
|
existing.cols.push(fk.source_column);
|
|
14416
14814
|
existing.refs.push(fk.target_column);
|
|
14417
14815
|
} else {
|
|
14418
|
-
result.set(
|
|
14816
|
+
result.set(key, {
|
|
14419
14817
|
source: fk.source_table,
|
|
14420
14818
|
target: fk.target_table,
|
|
14819
|
+
targetSchema: fk.target_schema,
|
|
14421
14820
|
cols: [fk.source_column],
|
|
14422
14821
|
refs: [fk.target_column],
|
|
14423
14822
|
deleteRule: fk.delete_rule,
|
|
@@ -14483,10 +14882,12 @@ function buildRLSMaps(rlsRows, policiesRows) {
|
|
|
14483
14882
|
function buildTableIR(tableName, ctx) {
|
|
14484
14883
|
const rawCols = ctx.tableColumns.get(tableName) ?? [];
|
|
14485
14884
|
const pkCols = ctx.tablePKs.get(tableName);
|
|
14885
|
+
const uniqueColumns = ctx.uniqueColumns.get(tableName);
|
|
14486
14886
|
const columns = rawCols.map((col) => {
|
|
14487
14887
|
const identity = col.is_identity === "YES" && col.identity_generation ? col.identity_generation === "ALWAYS" ? "always" : "byDefault" : void 0;
|
|
14488
14888
|
const collation = col.collation_name && col.collation_name !== "default" ? col.collation_name : void 0;
|
|
14489
14889
|
const colComment = ctx.columnComments.get(`${tableName}.${col.column_name}`) ?? void 0;
|
|
14890
|
+
const uniqueConstraintName = uniqueColumns?.get(col.column_name);
|
|
14490
14891
|
return {
|
|
14491
14892
|
name: col.column_name,
|
|
14492
14893
|
type: mapPgType(col.data_type, col.udt_name),
|
|
@@ -14496,6 +14897,7 @@ function buildTableIR(tableName, ctx) {
|
|
|
14496
14897
|
// of quoting it as a string literal.
|
|
14497
14898
|
...col.column_default != null ? { default: { sql: col.column_default } } : {},
|
|
14498
14899
|
originalDbType: col.udt_name,
|
|
14900
|
+
...uniqueConstraintName !== void 0 ? { unique: true, uniqueConstraintName } : {},
|
|
14499
14901
|
...collation ? { collation } : {},
|
|
14500
14902
|
...identity ? { identity } : {},
|
|
14501
14903
|
...colComment ? { comment: colComment } : {}
|
|
@@ -14504,12 +14906,17 @@ function buildTableIR(tableName, ctx) {
|
|
|
14504
14906
|
const foreignKeys = [];
|
|
14505
14907
|
for (const [, fk] of ctx.fksByConstraint) {
|
|
14506
14908
|
if (fk.source !== tableName) continue;
|
|
14507
|
-
|
|
14909
|
+
const isCrossSchema = fk.targetSchema !== void 0 && fk.targetSchema !== ctx.schema;
|
|
14910
|
+
if (!isCrossSchema && !ctx.tableNames.includes(fk.target)) continue;
|
|
14508
14911
|
const onDelete = mapDeleteRule(fk.deleteRule);
|
|
14509
14912
|
const onUpdate = mapDeleteRule(fk.updateRule);
|
|
14510
14913
|
foreignKeys.push({
|
|
14511
14914
|
columns: fk.cols,
|
|
14512
|
-
references: {
|
|
14915
|
+
references: {
|
|
14916
|
+
table: fk.target,
|
|
14917
|
+
columns: fk.refs,
|
|
14918
|
+
...isCrossSchema ? { schema: fk.targetSchema } : {}
|
|
14919
|
+
},
|
|
14513
14920
|
...onDelete !== "NO ACTION" ? { onDelete } : {},
|
|
14514
14921
|
...onUpdate !== "NO ACTION" ? { onUpdate } : {},
|
|
14515
14922
|
...fk.deferred ? { deferred: true } : {}
|
|
@@ -14557,6 +14964,7 @@ async function introspect(pool, options) {
|
|
|
14557
14964
|
const tablePartitions = buildPartitionMap(raw.partitions);
|
|
14558
14965
|
const tableChecks = buildCheckMap(raw.checks);
|
|
14559
14966
|
const tableIndexes = buildIndexMap(raw.indexes);
|
|
14967
|
+
const uniqueColumns = buildUniqueColumnMap(raw.uniqueColumns);
|
|
14560
14968
|
const tableColumns = buildColumnMap(raw.columns);
|
|
14561
14969
|
const tablePKs = buildPKMap(raw.pks);
|
|
14562
14970
|
const fksByConstraint = buildFKMap(raw.fks);
|
|
@@ -14575,6 +14983,7 @@ async function introspect(pool, options) {
|
|
|
14575
14983
|
tablePKs,
|
|
14576
14984
|
fksByConstraint,
|
|
14577
14985
|
tableIndexes,
|
|
14986
|
+
uniqueColumns,
|
|
14578
14987
|
tableChecks,
|
|
14579
14988
|
tableComments,
|
|
14580
14989
|
columnComments,
|
|
@@ -14582,20 +14991,33 @@ async function introspect(pool, options) {
|
|
|
14582
14991
|
tableRlsEnabled,
|
|
14583
14992
|
tablePolicies,
|
|
14584
14993
|
tableNames,
|
|
14994
|
+
schema,
|
|
14585
14995
|
warnings
|
|
14586
14996
|
});
|
|
14587
14997
|
tables.set(tableName, table);
|
|
14588
14998
|
}
|
|
14589
14999
|
const extensions = raw.extensions.map((r) => r.name);
|
|
14590
15000
|
const sequenceMap = buildSequenceMap(raw.sequences);
|
|
14591
|
-
const relations = inferRelations(fksByConstraint, tableNames);
|
|
14592
|
-
const hierarchies = detectHierarchies(
|
|
15001
|
+
const relations = inferRelations(fksByConstraint, tableNames, schema);
|
|
15002
|
+
const hierarchies = detectHierarchies(
|
|
15003
|
+
tables,
|
|
15004
|
+
fksByConstraint,
|
|
15005
|
+
tableNames,
|
|
15006
|
+
schema
|
|
15007
|
+
);
|
|
15008
|
+
const externalTables = /* @__PURE__ */ new Set();
|
|
15009
|
+
for (const fk of fksByConstraint.values()) {
|
|
15010
|
+
if (fk.targetSchema !== void 0 && fk.targetSchema !== schema && !tables.has(fk.target)) {
|
|
15011
|
+
externalTables.add(fk.target);
|
|
15012
|
+
}
|
|
15013
|
+
}
|
|
14593
15014
|
const modelIR = new ModelIRImpl(
|
|
14594
15015
|
tables,
|
|
14595
15016
|
relations,
|
|
14596
15017
|
enumMap,
|
|
14597
15018
|
extensions,
|
|
14598
|
-
sequenceMap
|
|
15019
|
+
sequenceMap,
|
|
15020
|
+
externalTables
|
|
14599
15021
|
);
|
|
14600
15022
|
return Object.assign(modelIR, {
|
|
14601
15023
|
hierarchies,
|
|
@@ -14603,10 +15025,11 @@ async function introspect(pool, options) {
|
|
|
14603
15025
|
warnings
|
|
14604
15026
|
});
|
|
14605
15027
|
}
|
|
14606
|
-
function inferRelations(fksByConstraint, filteredTables) {
|
|
15028
|
+
function inferRelations(fksByConstraint, filteredTables, schema) {
|
|
14607
15029
|
const relations = /* @__PURE__ */ new Map();
|
|
14608
15030
|
const filteredSet = new Set(filteredTables);
|
|
14609
15031
|
for (const [, fk] of fksByConstraint) {
|
|
15032
|
+
if (fk.targetSchema !== void 0 && fk.targetSchema !== schema) continue;
|
|
14610
15033
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
14611
15034
|
const belongsToName = deriveRelationName(fk.cols[0], fk.target);
|
|
14612
15035
|
const relationFk = {
|
|
@@ -14656,13 +15079,14 @@ function deriveRelationName(fkColumn, _targetTable) {
|
|
|
14656
15079
|
}
|
|
14657
15080
|
return fkColumn;
|
|
14658
15081
|
}
|
|
14659
|
-
function detectHierarchies(tables, fksByConstraint, filteredTables) {
|
|
15082
|
+
function detectHierarchies(tables, fksByConstraint, filteredTables, schema) {
|
|
14660
15083
|
const hierarchies = [];
|
|
14661
15084
|
const filteredSet = new Set(filteredTables);
|
|
14662
15085
|
const fksBySourceTarget = /* @__PURE__ */ new Map();
|
|
14663
15086
|
for (const [, fk] of fksByConstraint) {
|
|
15087
|
+
if (fk.targetSchema !== void 0 && fk.targetSchema !== schema) continue;
|
|
14664
15088
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
14665
|
-
if (fk.source === fk.target && fk.cols.length === 1) {
|
|
15089
|
+
if (fk.source === fk.target && fk.cols.length === 1 && (fk.targetSchema === void 0 || fk.targetSchema === schema)) {
|
|
14666
15090
|
const table = tables.get(fk.source);
|
|
14667
15091
|
const pk = table?.primaryKey;
|
|
14668
15092
|
const nodeIdColumn = typeof pk === "string" ? pk : pk?.[0] ?? DEFAULT_PK_COLUMN;
|
|
@@ -16065,10 +16489,9 @@ function convertDottedFieldsToExists(decisions, rootTable, model) {
|
|
|
16065
16489
|
...resolved.relationType && { relationType: resolved.relationType },
|
|
16066
16490
|
conditions: [
|
|
16067
16491
|
{
|
|
16492
|
+
...d,
|
|
16068
16493
|
type: "where",
|
|
16069
16494
|
column: targetColumn,
|
|
16070
|
-
operator: d.operator,
|
|
16071
|
-
value: d.value,
|
|
16072
16495
|
table: resolved.target
|
|
16073
16496
|
}
|
|
16074
16497
|
]
|
|
@@ -16938,14 +17361,16 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
|
|
|
16938
17361
|
schemaForFromName(schemaName, intent.table, deps.bindingNames, naming),
|
|
16939
17362
|
naming
|
|
16940
17363
|
);
|
|
16941
|
-
|
|
17364
|
+
const joinDecision = {
|
|
16942
17365
|
type: "join",
|
|
16943
17366
|
targetTable: intent.table,
|
|
16944
17367
|
alias: tableAlias,
|
|
16945
17368
|
joinType: intent.type,
|
|
16946
17369
|
joinRarg: joinedRangeVar,
|
|
16947
|
-
joinOnNode: onNode
|
|
16948
|
-
|
|
17370
|
+
joinOnNode: onNode,
|
|
17371
|
+
joinOnParams: paramState.parameters
|
|
17372
|
+
};
|
|
17373
|
+
results.push(joinDecision);
|
|
16949
17374
|
}
|
|
16950
17375
|
}
|
|
16951
17376
|
return results;
|
|
@@ -18152,8 +18577,9 @@ function qualifyTable3(table, schema) {
|
|
|
18152
18577
|
return schema ? `${quoteIdent2(schema, "schema")}.${quoteIdent2(table, "table")}` : quoteIdent2(table, "table");
|
|
18153
18578
|
}
|
|
18154
18579
|
function generateCreateIndexSQL(table, options, schema) {
|
|
18155
|
-
|
|
18156
|
-
|
|
18580
|
+
const indexMethod = options.method;
|
|
18581
|
+
if (indexMethod !== void 0) {
|
|
18582
|
+
validateIndexMethod(indexMethod, "index method");
|
|
18157
18583
|
}
|
|
18158
18584
|
const parts = ["CREATE"];
|
|
18159
18585
|
if (options.unique) parts.push("UNIQUE");
|
|
@@ -18162,8 +18588,8 @@ function generateCreateIndexSQL(table, options, schema) {
|
|
|
18162
18588
|
if (options.ifNotExists) parts.push("IF NOT EXISTS");
|
|
18163
18589
|
parts.push(quoteIdentifier2(options.name));
|
|
18164
18590
|
parts.push(`ON ${qualifyTable3(table, schema)}`);
|
|
18165
|
-
if (
|
|
18166
|
-
parts.push(`USING ${
|
|
18591
|
+
if (indexMethod) {
|
|
18592
|
+
parts.push(`USING ${indexMethod}`);
|
|
18167
18593
|
}
|
|
18168
18594
|
const colParts = [];
|
|
18169
18595
|
for (const col of options.columns) {
|
|
@@ -18180,13 +18606,14 @@ function generateCreateIndexSQL(table, options, schema) {
|
|
|
18180
18606
|
if (options.with && Object.keys(options.with).length > 0) {
|
|
18181
18607
|
const withParams = Object.entries(options.with).map(([k, v]) => {
|
|
18182
18608
|
validateIdentifier(k, "alias");
|
|
18183
|
-
return `${k} = ${v}`;
|
|
18609
|
+
return `${k} = ${formatStorageParameterValue(v, `index WITH parameter "${k}"`)}`;
|
|
18184
18610
|
}).join(", ");
|
|
18185
18611
|
parts.push(`WITH (${withParams})`);
|
|
18186
18612
|
}
|
|
18187
|
-
|
|
18188
|
-
|
|
18189
|
-
|
|
18613
|
+
const whereExpr = options.where;
|
|
18614
|
+
if (whereExpr) {
|
|
18615
|
+
validateSqlExpression(whereExpr, "index WHERE predicate");
|
|
18616
|
+
parts.push(`WHERE ${whereExpr}`);
|
|
18190
18617
|
}
|
|
18191
18618
|
return parts.join(" ");
|
|
18192
18619
|
}
|
|
@@ -18197,10 +18624,11 @@ function buildColumnPart(col, opclassMap) {
|
|
|
18197
18624
|
if (opclass2) validateIdentifier(opclass2, "alias");
|
|
18198
18625
|
return opclass2 ? `${quoted} ${opclass2}` : quoted;
|
|
18199
18626
|
}
|
|
18200
|
-
|
|
18627
|
+
const expression = col.expression;
|
|
18628
|
+
validateSqlExpression(expression, "index expression");
|
|
18201
18629
|
const opclass = col.opclass;
|
|
18202
18630
|
if (opclass) validateIdentifier(opclass, "alias");
|
|
18203
|
-
return opclass ? `${
|
|
18631
|
+
return opclass ? `${expression} ${opclass}` : expression;
|
|
18204
18632
|
}
|
|
18205
18633
|
function generateDropIndexSQL(name, options) {
|
|
18206
18634
|
const parts = ["DROP INDEX"];
|