@dbsp/adapter-pgsql 1.10.2 → 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 +604 -173
- 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]);
|
|
@@ -11008,7 +11185,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11008
11185
|
// --------------------------------------------------------------------------
|
|
11009
11186
|
// CASE Expression Compilation
|
|
11010
11187
|
// --------------------------------------------------------------------------
|
|
11011
|
-
compileCaseExpression(decision) {
|
|
11188
|
+
compileCaseExpression(decision, plan) {
|
|
11012
11189
|
const conditions = decision.conditions;
|
|
11013
11190
|
const elseValue = decision.value;
|
|
11014
11191
|
if (!conditions || conditions.length === 0) {
|
|
@@ -11016,7 +11193,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11016
11193
|
}
|
|
11017
11194
|
const args = conditions.map((cond) => {
|
|
11018
11195
|
const whenExpr = this.dispatchWhere(cond.when);
|
|
11019
|
-
const thenResult = this.compileCaseValue(cond.then);
|
|
11196
|
+
const thenResult = this.compileCaseValue(cond.then, plan);
|
|
11020
11197
|
return {
|
|
11021
11198
|
CaseWhen: {
|
|
11022
11199
|
expr: whenExpr,
|
|
@@ -11026,7 +11203,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11026
11203
|
});
|
|
11027
11204
|
let defresult;
|
|
11028
11205
|
if (elseValue !== void 0) {
|
|
11029
|
-
defresult = this.compileCaseValue(elseValue);
|
|
11206
|
+
defresult = this.compileCaseValue(elseValue, plan);
|
|
11030
11207
|
}
|
|
11031
11208
|
return {
|
|
11032
11209
|
CaseExpr: {
|
|
@@ -11039,26 +11216,66 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
11039
11216
|
* Compile a CASE THEN/ELSE value based on its ExpressionIntent kind.
|
|
11040
11217
|
* Delegates to shared resolveCaseValue with nested CASE support.
|
|
11041
11218
|
*/
|
|
11042
|
-
compileCaseValue(value) {
|
|
11219
|
+
compileCaseValue(value, plan) {
|
|
11043
11220
|
return resolveCaseValue(
|
|
11044
11221
|
value,
|
|
11045
11222
|
this.currentRootTable,
|
|
11046
11223
|
void 0,
|
|
11047
11224
|
this.naming,
|
|
11048
11225
|
this.state,
|
|
11049
|
-
(expr) =>
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
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
|
+
)
|
|
11060
11244
|
);
|
|
11061
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
|
+
}
|
|
11062
11279
|
// --------------------------------------------------------------------------
|
|
11063
11280
|
// INSERT Compilation
|
|
11064
11281
|
// --------------------------------------------------------------------------
|
|
@@ -11261,6 +11478,9 @@ function compilePlan(plan, options) {
|
|
|
11261
11478
|
init_naming_plugin();
|
|
11262
11479
|
init_validate();
|
|
11263
11480
|
|
|
11481
|
+
// src/ddl/phases/comments.ts
|
|
11482
|
+
init_validate();
|
|
11483
|
+
|
|
11264
11484
|
// src/ddl/phases/types.ts
|
|
11265
11485
|
function sup(caps, flag) {
|
|
11266
11486
|
return !caps || flag === true;
|
|
@@ -11281,6 +11501,7 @@ var VALID_INDEX_METHODS = /* @__PURE__ */ new Set([
|
|
|
11281
11501
|
"bloom"
|
|
11282
11502
|
]);
|
|
11283
11503
|
function validateIndexMethod(method, context = "index method") {
|
|
11504
|
+
assertString(method, `Invalid ${context}`);
|
|
11284
11505
|
if (!VALID_INDEX_METHODS.has(method)) {
|
|
11285
11506
|
throw new Error(
|
|
11286
11507
|
`Invalid ${context}: "${method}". Must be one of: ${[...VALID_INDEX_METHODS].join(", ")}`
|
|
@@ -11288,6 +11509,7 @@ function validateIndexMethod(method, context = "index method") {
|
|
|
11288
11509
|
}
|
|
11289
11510
|
}
|
|
11290
11511
|
function validateEnumLabel(value, context = "enum label") {
|
|
11512
|
+
assertString(value, `Invalid ${context}`);
|
|
11291
11513
|
if (/\x00/.test(value)) {
|
|
11292
11514
|
throw new Error(
|
|
11293
11515
|
`Invalid ${context}: contains NUL byte (\\x00) which would be silently truncated by PostgreSQL`
|
|
@@ -11312,6 +11534,7 @@ function quoteCollation(name) {
|
|
|
11312
11534
|
return `"${name}"`;
|
|
11313
11535
|
}
|
|
11314
11536
|
function quoteRoleName(name) {
|
|
11537
|
+
assertString(name, "quoteRoleName: role name");
|
|
11315
11538
|
if (!name) {
|
|
11316
11539
|
throw new Error("quoteRoleName: role name must not be empty");
|
|
11317
11540
|
}
|
|
@@ -11378,17 +11601,21 @@ function generateCommentsPhase(ctx) {
|
|
|
11378
11601
|
}
|
|
11379
11602
|
const statements = [];
|
|
11380
11603
|
for (const table of tables) {
|
|
11381
|
-
|
|
11604
|
+
const tableComment = table.comment;
|
|
11605
|
+
if (tableComment) {
|
|
11606
|
+
assertString(tableComment, "table comment");
|
|
11382
11607
|
const qualifiedTable = qualifyTableIdent(table.name, schemaName, naming);
|
|
11383
11608
|
statements.push(
|
|
11384
|
-
`COMMENT ON TABLE ${qualifiedTable} IS '${
|
|
11609
|
+
`COMMENT ON TABLE ${qualifiedTable} IS '${tableComment.replace(/'/g, "''")}';`
|
|
11385
11610
|
);
|
|
11386
11611
|
}
|
|
11387
11612
|
for (const col of table.columns) {
|
|
11388
|
-
|
|
11613
|
+
const columnComment = col.comment;
|
|
11614
|
+
if (columnComment) {
|
|
11615
|
+
assertString(columnComment, "column comment");
|
|
11389
11616
|
const qualifiedTable = qualifyTableIdent(table.name, schemaName, naming);
|
|
11390
11617
|
statements.push(
|
|
11391
|
-
`COMMENT ON COLUMN ${qualifiedTable}.${quoteIdent2(naming.toDatabase(col.name))} IS '${
|
|
11618
|
+
`COMMENT ON COLUMN ${qualifiedTable}.${quoteIdent2(naming.toDatabase(col.name))} IS '${columnComment.replace(/'/g, "''")}';`
|
|
11392
11619
|
);
|
|
11393
11620
|
}
|
|
11394
11621
|
}
|
|
@@ -11413,9 +11640,10 @@ function generateConstraintsPhase(ctx) {
|
|
|
11413
11640
|
for (const check of table.checkConstraints ?? []) {
|
|
11414
11641
|
const qualifiedTable = qualifyTableIdent(table.name, schemaName, naming);
|
|
11415
11642
|
const constraintName = quoteIdent2(check.name);
|
|
11416
|
-
|
|
11643
|
+
const expression = check.expression;
|
|
11644
|
+
validateCheckExpression(expression, "check constraint expression");
|
|
11417
11645
|
statements.push(
|
|
11418
|
-
`ALTER TABLE ${qualifiedTable} ADD CONSTRAINT ${constraintName} ${
|
|
11646
|
+
`ALTER TABLE ${qualifiedTable} ADD CONSTRAINT ${constraintName} ${expression};`
|
|
11419
11647
|
);
|
|
11420
11648
|
}
|
|
11421
11649
|
}
|
|
@@ -11598,12 +11826,29 @@ function mapOnDeleteAction(action) {
|
|
|
11598
11826
|
function qualifyTable(table, schemaName) {
|
|
11599
11827
|
return schemaName ? `${quoteIdent2(schemaName, "schema")}.${quoteIdent2(table, "table")}` : quoteIdent2(table, "table");
|
|
11600
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
|
+
}
|
|
11601
11843
|
function pkName(table) {
|
|
11602
11844
|
return `pk_${table}`;
|
|
11603
11845
|
}
|
|
11604
11846
|
function fkName(table, columns) {
|
|
11605
11847
|
return `fk_${table}_${columns.join("_")}`;
|
|
11606
11848
|
}
|
|
11849
|
+
function uniqueName(table, column) {
|
|
11850
|
+
return `${table}_${column}_key`;
|
|
11851
|
+
}
|
|
11607
11852
|
function idxName(table, columns, customName) {
|
|
11608
11853
|
return customName ?? `idx_${table}_${columns.join("_")}`;
|
|
11609
11854
|
}
|
|
@@ -11658,11 +11903,22 @@ function buildPolicySQL(tableName, policy, schemaName) {
|
|
|
11658
11903
|
}
|
|
11659
11904
|
function buildSequenceClause(verb, seqName, seq, includeCycleNoCycle = false) {
|
|
11660
11905
|
const parts = [`${verb} ${seqName}`];
|
|
11661
|
-
if (seq.startWith !== void 0)
|
|
11906
|
+
if (seq.startWith !== void 0)
|
|
11907
|
+
parts.push(
|
|
11908
|
+
`START WITH ${assertNumericLiteral(seq.startWith, "sequence START WITH")}`
|
|
11909
|
+
);
|
|
11662
11910
|
if (seq.incrementBy !== void 0)
|
|
11663
|
-
parts.push(
|
|
11664
|
-
|
|
11665
|
-
|
|
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
|
+
);
|
|
11666
11922
|
if (includeCycleNoCycle) {
|
|
11667
11923
|
if (seq.cycle !== void 0) parts.push(seq.cycle ? "CYCLE" : "NO CYCLE");
|
|
11668
11924
|
} else {
|
|
@@ -11672,7 +11928,7 @@ function buildSequenceClause(verb, seqName, seq, includeCycleNoCycle = false) {
|
|
|
11672
11928
|
}
|
|
11673
11929
|
function failSafeUnknownDownChange(kind) {
|
|
11674
11930
|
return {
|
|
11675
|
-
sql: `-- WARNING: Cannot reverse unsupported SchemaChange kind "${kind}"`,
|
|
11931
|
+
sql: `-- WARNING: Cannot reverse unsupported SchemaChange kind "${sanitizeCommentText(kind)}"`,
|
|
11676
11932
|
destructive: true
|
|
11677
11933
|
};
|
|
11678
11934
|
}
|
|
@@ -11734,7 +11990,7 @@ function generateMigrationSQL(diff, options) {
|
|
|
11734
11990
|
[],
|
|
11735
11991
|
// 8: alter column
|
|
11736
11992
|
[],
|
|
11737
|
-
// 9: add PK
|
|
11993
|
+
// 9: add PK / column UNIQUE constraint
|
|
11738
11994
|
[],
|
|
11739
11995
|
// 10: add FK
|
|
11740
11996
|
[],
|
|
@@ -11821,6 +12077,7 @@ function getPhase(kind) {
|
|
|
11821
12077
|
case "alter_column_collation":
|
|
11822
12078
|
case "alter_column_identity":
|
|
11823
12079
|
return 8;
|
|
12080
|
+
case "alter_column_unique":
|
|
11824
12081
|
case "add_primary_key":
|
|
11825
12082
|
return 9;
|
|
11826
12083
|
case "add_foreign_key":
|
|
@@ -11857,7 +12114,7 @@ function upAddColumn(change, schemaName) {
|
|
|
11857
12114
|
}
|
|
11858
12115
|
function upAlterColumnType(change, schemaName) {
|
|
11859
12116
|
const col = change.meta?.column;
|
|
11860
|
-
const toType = col ? mapColumnType(col) :
|
|
12117
|
+
const toType = col ? mapColumnType(col) : validateDbTypeName(change.meta?.toType);
|
|
11861
12118
|
return `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} TYPE ${toType};`;
|
|
11862
12119
|
}
|
|
11863
12120
|
function upAlterColumnNullable(change, schemaName) {
|
|
@@ -11872,6 +12129,34 @@ function upAlterColumnDefault(change, schemaName) {
|
|
|
11872
12129
|
}
|
|
11873
12130
|
return `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} SET DEFAULT ${formatDefault(def)};`;
|
|
11874
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
|
+
}
|
|
11875
12160
|
function upAddPrimaryKey(change, schemaName) {
|
|
11876
12161
|
const columns = change.meta?.columns;
|
|
11877
12162
|
const pkCols = columns.map((n) => quoteIdent2(n, "alias")).join(", ");
|
|
@@ -11900,8 +12185,9 @@ function upCreateIndex(change, schemaName) {
|
|
|
11900
12185
|
"alias"
|
|
11901
12186
|
);
|
|
11902
12187
|
const unique = idx.unique ? "UNIQUE " : "";
|
|
11903
|
-
|
|
11904
|
-
|
|
12188
|
+
const indexMethod = idx.method;
|
|
12189
|
+
if (indexMethod) validateIndexMethod(indexMethod, "index method");
|
|
12190
|
+
const method = indexMethod ? ` USING ${indexMethod}` : "";
|
|
11905
12191
|
const colParts = [
|
|
11906
12192
|
...(idx.expressions ?? []).map((expr) => {
|
|
11907
12193
|
validateSqlExpression(expr, "index expression");
|
|
@@ -11918,10 +12204,11 @@ function upCreateIndex(change, schemaName) {
|
|
|
11918
12204
|
const nullsNotDistinct = idx.unique && idx.nullsNotDistinct ? " NULLS NOT DISTINCT" : "";
|
|
11919
12205
|
const withParams = idx.with && Object.keys(idx.with).length > 0 ? ` WITH (${Object.entries(idx.with).map(([k, v]) => {
|
|
11920
12206
|
validateIdentifier(k, "alias");
|
|
11921
|
-
return `${k} = ${v}`;
|
|
12207
|
+
return `${k} = ${formatStorageParameterValue(v, `index WITH parameter "${k}"`)}`;
|
|
11922
12208
|
}).join(", ")})` : "";
|
|
11923
|
-
|
|
11924
|
-
|
|
12209
|
+
const whereExpr = idx.where;
|
|
12210
|
+
if (whereExpr) validateSqlExpression(whereExpr, "index WHERE predicate");
|
|
12211
|
+
const where = whereExpr ? ` WHERE ${whereExpr}` : "";
|
|
11925
12212
|
return `CREATE ${unique}INDEX IF NOT EXISTS ${indexName} ON ${qualifyTable(change.table, schemaName)}${method} (${cols})${include}${nullsNotDistinct}${withParams}${where};`;
|
|
11926
12213
|
}
|
|
11927
12214
|
function upDropIndex(change, schemaName) {
|
|
@@ -11937,12 +12224,12 @@ function upDropIndex(change, schemaName) {
|
|
|
11937
12224
|
function upAddCheckConstraint(change, schemaName) {
|
|
11938
12225
|
const check = change.meta?.check;
|
|
11939
12226
|
if (!check) return void 0;
|
|
12227
|
+
const expression = check.expression;
|
|
11940
12228
|
const notValid = check.notValid ? " NOT VALID" : "";
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
"
|
|
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"
|
|
11944
12232
|
);
|
|
11945
|
-
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 $$;";
|
|
11946
12233
|
}
|
|
11947
12234
|
function upValidateConstraint(change, schemaName) {
|
|
11948
12235
|
const fk = change.meta?.fk;
|
|
@@ -12011,6 +12298,7 @@ function upAlterColumnIdentity(change, schemaName) {
|
|
|
12011
12298
|
function upAddComment(change, schemaName) {
|
|
12012
12299
|
const comment = change.meta?.comment;
|
|
12013
12300
|
const target = change.meta?.target;
|
|
12301
|
+
assertString(comment, "migration comment");
|
|
12014
12302
|
const escaped = comment.replace(/'/g, "''");
|
|
12015
12303
|
if (target === "table") {
|
|
12016
12304
|
return `COMMENT ON TABLE ${qualifyTable(change.table, schemaName)} IS '${escaped}';`;
|
|
@@ -12038,6 +12326,8 @@ function changeToUpSQL(change, schemaName) {
|
|
|
12038
12326
|
return upAlterColumnNullable(change, schemaName);
|
|
12039
12327
|
case "alter_column_default":
|
|
12040
12328
|
return upAlterColumnDefault(change, schemaName);
|
|
12329
|
+
case "alter_column_unique":
|
|
12330
|
+
return upAlterColumnUnique(change, schemaName);
|
|
12041
12331
|
case "add_primary_key":
|
|
12042
12332
|
return upAddPrimaryKey(change, schemaName);
|
|
12043
12333
|
case "drop_primary_key":
|
|
@@ -12135,7 +12425,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12135
12425
|
};
|
|
12136
12426
|
case "drop_table":
|
|
12137
12427
|
return {
|
|
12138
|
-
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`,
|
|
12139
12429
|
destructive: true
|
|
12140
12430
|
};
|
|
12141
12431
|
case "add_column":
|
|
@@ -12145,19 +12435,20 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12145
12435
|
};
|
|
12146
12436
|
case "drop_column":
|
|
12147
12437
|
return {
|
|
12148
|
-
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`,
|
|
12149
12439
|
destructive: true
|
|
12150
12440
|
};
|
|
12151
12441
|
case "alter_column_type": {
|
|
12152
12442
|
const fromType = change.meta?.fromType;
|
|
12153
|
-
if (
|
|
12443
|
+
if (fromType == null) {
|
|
12154
12444
|
return {
|
|
12155
|
-
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`,
|
|
12156
12446
|
destructive: true
|
|
12157
12447
|
};
|
|
12158
12448
|
}
|
|
12449
|
+
const safeFromType = validateDbTypeName(fromType);
|
|
12159
12450
|
return {
|
|
12160
|
-
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};`,
|
|
12161
12452
|
destructive: true
|
|
12162
12453
|
};
|
|
12163
12454
|
}
|
|
@@ -12165,7 +12456,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12165
12456
|
const oldNullable = change.meta?.oldNullable;
|
|
12166
12457
|
if (oldNullable === void 0) {
|
|
12167
12458
|
return {
|
|
12168
|
-
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`,
|
|
12169
12460
|
destructive: true
|
|
12170
12461
|
};
|
|
12171
12462
|
}
|
|
@@ -12180,7 +12471,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12180
12471
|
const oldDefault = change.meta?.oldDefault;
|
|
12181
12472
|
if (oldDefault === void 0) {
|
|
12182
12473
|
return {
|
|
12183
|
-
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`,
|
|
12184
12475
|
destructive: true
|
|
12185
12476
|
};
|
|
12186
12477
|
}
|
|
@@ -12197,6 +12488,30 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12197
12488
|
destructive: false
|
|
12198
12489
|
};
|
|
12199
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
|
+
}
|
|
12200
12515
|
case "add_primary_key": {
|
|
12201
12516
|
return {
|
|
12202
12517
|
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP CONSTRAINT IF EXISTS ${quoteIdent2(pkName(change.table), "alias")} CASCADE;`,
|
|
@@ -12214,7 +12529,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12214
12529
|
};
|
|
12215
12530
|
}
|
|
12216
12531
|
return {
|
|
12217
|
-
sql: `-- WARNING: Cannot reverse drop_primary_key "${change.table}" -- columns unknown`,
|
|
12532
|
+
sql: `-- WARNING: Cannot reverse drop_primary_key "${sanitizeCommentText(change.table)}" -- columns unknown`,
|
|
12218
12533
|
destructive: true
|
|
12219
12534
|
};
|
|
12220
12535
|
}
|
|
@@ -12234,7 +12549,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12234
12549
|
const fk = change.meta?.fk;
|
|
12235
12550
|
if (!fk) {
|
|
12236
12551
|
return {
|
|
12237
|
-
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`,
|
|
12238
12553
|
destructive: true
|
|
12239
12554
|
};
|
|
12240
12555
|
}
|
|
@@ -12249,7 +12564,7 @@ function changeToDownSQL(change, schemaName) {
|
|
|
12249
12564
|
const fk = change.meta?.fk;
|
|
12250
12565
|
if (!oldFk || !fk) {
|
|
12251
12566
|
return {
|
|
12252
|
-
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`,
|
|
12253
12568
|
destructive: true
|
|
12254
12569
|
};
|
|
12255
12570
|
}
|
|
@@ -12279,7 +12594,7 @@ ${add}`, destructive: false };
|
|
|
12279
12594
|
const idx = change.meta?.index;
|
|
12280
12595
|
if (!idx) {
|
|
12281
12596
|
return {
|
|
12282
|
-
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`,
|
|
12283
12598
|
destructive: true
|
|
12284
12599
|
};
|
|
12285
12600
|
}
|
|
@@ -12300,12 +12615,12 @@ ${add}`, destructive: false };
|
|
|
12300
12615
|
case "drop_check_constraint": {
|
|
12301
12616
|
const check = change.meta?.check;
|
|
12302
12617
|
if (!check) return { sql: void 0, destructive: true };
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
"migration check constraint (down)"
|
|
12306
|
-
);
|
|
12618
|
+
const expression = check.expression;
|
|
12619
|
+
validateCheckExpression(expression, "migration check constraint (down)");
|
|
12307
12620
|
return {
|
|
12308
|
-
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
|
+
),
|
|
12309
12624
|
// Allowlisted: re-adds the dropped CHECK constraint from metadata.
|
|
12310
12625
|
destructive: false
|
|
12311
12626
|
};
|
|
@@ -12323,7 +12638,10 @@ ${add}`, destructive: false };
|
|
|
12323
12638
|
const enumDef = change.meta?.enum;
|
|
12324
12639
|
if (!enumDef) return { sql: void 0, destructive: true };
|
|
12325
12640
|
const enumName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(enumDef.name, "alias")}` : quoteIdent2(enumDef.name, "alias");
|
|
12326
|
-
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(", ");
|
|
12327
12645
|
return {
|
|
12328
12646
|
sql: `CREATE TYPE ${enumName} AS ENUM (${values});`,
|
|
12329
12647
|
// Allowlisted: re-creates the dropped enum type from metadata.
|
|
@@ -12337,7 +12655,7 @@ ${add}`, destructive: false };
|
|
|
12337
12655
|
};
|
|
12338
12656
|
case "alter_column_collation": {
|
|
12339
12657
|
return {
|
|
12340
|
-
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`,
|
|
12341
12659
|
destructive: true
|
|
12342
12660
|
};
|
|
12343
12661
|
}
|
|
@@ -12363,7 +12681,7 @@ ${add}`, destructive: false };
|
|
|
12363
12681
|
}
|
|
12364
12682
|
if (!prevIdentity && !col.identity) {
|
|
12365
12683
|
return {
|
|
12366
|
-
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`,
|
|
12367
12685
|
destructive: true
|
|
12368
12686
|
};
|
|
12369
12687
|
}
|
|
@@ -12390,7 +12708,8 @@ ${add}`, destructive: false };
|
|
|
12390
12708
|
case "drop_comment": {
|
|
12391
12709
|
const target = change.meta?.target;
|
|
12392
12710
|
const comment = change.meta?.comment;
|
|
12393
|
-
if (
|
|
12711
|
+
if (comment !== void 0) {
|
|
12712
|
+
assertString(comment, "migration comment");
|
|
12394
12713
|
const escaped = comment.replace(/'/g, "''");
|
|
12395
12714
|
if (target === "table") {
|
|
12396
12715
|
return {
|
|
@@ -12407,8 +12726,9 @@ ${add}`, destructive: false };
|
|
|
12407
12726
|
};
|
|
12408
12727
|
}
|
|
12409
12728
|
}
|
|
12729
|
+
const columnTarget2 = change.column === void 0 ? "" : `."${sanitizeCommentText(change.column)}"`;
|
|
12410
12730
|
return {
|
|
12411
|
-
sql:
|
|
12731
|
+
sql: '-- WARNING: Cannot reverse drop_comment "' + sanitizeCommentText(change.table) + '"' + columnTarget2 + " -- comment text was lost",
|
|
12412
12732
|
destructive: true
|
|
12413
12733
|
};
|
|
12414
12734
|
}
|
|
@@ -12442,7 +12762,7 @@ ${add}`, destructive: false };
|
|
|
12442
12762
|
const prevSeq = change.meta?.previousSequence;
|
|
12443
12763
|
if (!prevSeq) {
|
|
12444
12764
|
return {
|
|
12445
|
-
sql: `-- WARNING: Cannot reverse alter_sequence "${change.table}" -- missing migration metadata`,
|
|
12765
|
+
sql: `-- WARNING: Cannot reverse alter_sequence "${sanitizeCommentText(change.table)}" -- missing migration metadata`,
|
|
12446
12766
|
destructive: true
|
|
12447
12767
|
};
|
|
12448
12768
|
}
|
|
@@ -12465,7 +12785,7 @@ ${add}`, destructive: false };
|
|
|
12465
12785
|
}
|
|
12466
12786
|
case "validate_constraint":
|
|
12467
12787
|
return {
|
|
12468
|
-
sql: `-- VALIDATE CONSTRAINT cannot be reversed in PostgreSQL (table: "${change.table}")`,
|
|
12788
|
+
sql: `-- VALIDATE CONSTRAINT cannot be reversed in PostgreSQL (table: "${sanitizeCommentText(change.table)}")`,
|
|
12469
12789
|
destructive: true
|
|
12470
12790
|
};
|
|
12471
12791
|
case "enable_rls":
|
|
@@ -12506,7 +12826,6 @@ function generateDownSQL(diff, options) {
|
|
|
12506
12826
|
function generateDownMigrationSQL(diff, options) {
|
|
12507
12827
|
const schemaName = options?.schemaName;
|
|
12508
12828
|
const includeDestructive = options?.includeDestructive ?? true;
|
|
12509
|
-
const changes = includeDestructive ? diff.changes : diff.changes.filter((c) => !c.destructive);
|
|
12510
12829
|
const phases = [
|
|
12511
12830
|
[],
|
|
12512
12831
|
// 0: drop FK, drop CHECK
|
|
@@ -12527,7 +12846,7 @@ function generateDownMigrationSQL(diff, options) {
|
|
|
12527
12846
|
[],
|
|
12528
12847
|
// 8: alter column
|
|
12529
12848
|
[],
|
|
12530
|
-
// 9: add PK
|
|
12849
|
+
// 9: add PK / column UNIQUE constraint
|
|
12531
12850
|
[],
|
|
12532
12851
|
// 10: add FK
|
|
12533
12852
|
[],
|
|
@@ -12547,7 +12866,7 @@ function generateDownMigrationSQL(diff, options) {
|
|
|
12547
12866
|
[]
|
|
12548
12867
|
// 18: CREATE/DROP POLICY
|
|
12549
12868
|
];
|
|
12550
|
-
for (const change of changes) {
|
|
12869
|
+
for (const change of diff.changes) {
|
|
12551
12870
|
const phase = getPhase(change.kind);
|
|
12552
12871
|
phases[phase].push(change);
|
|
12553
12872
|
}
|
|
@@ -12556,6 +12875,7 @@ function generateDownMigrationSQL(diff, options) {
|
|
|
12556
12875
|
for (let i = phases.length - 1; i >= 0; i--) {
|
|
12557
12876
|
for (const change of phases[i]) {
|
|
12558
12877
|
const down = changeToDownSQL(change, schemaName);
|
|
12878
|
+
if (!includeDestructive && down.destructive) continue;
|
|
12559
12879
|
destructive = down.destructive || destructive;
|
|
12560
12880
|
if (down.sql) statements.push(down.sql);
|
|
12561
12881
|
}
|
|
@@ -12600,7 +12920,10 @@ function generateAddFKSQL(tableName, fk, schemaName) {
|
|
|
12600
12920
|
const qualTable = qualifyTable(tableName, schemaName);
|
|
12601
12921
|
const constraintName = quoteIdent2(fkName(tableName, fk.columns), "alias");
|
|
12602
12922
|
const fkCols = fk.columns.map((n) => quoteIdent2(n, "alias")).join(", ");
|
|
12603
|
-
const refTable = qualifyTable(
|
|
12923
|
+
const refTable = qualifyTable(
|
|
12924
|
+
fk.references.table,
|
|
12925
|
+
fk.references.schema ?? schemaName
|
|
12926
|
+
);
|
|
12604
12927
|
const refCols = fk.references.columns.map((n) => quoteIdent2(n, "alias")).join(", ");
|
|
12605
12928
|
const onDelete = fk.onDelete ? ` ON DELETE ${mapOnDeleteAction(fk.onDelete)}` : "";
|
|
12606
12929
|
const onUpdate = fk.onUpdate ? ` ON UPDATE ${mapOnDeleteAction(fk.onUpdate)}` : "";
|
|
@@ -12686,10 +13009,11 @@ function quoteIdentifier(name) {
|
|
|
12686
13009
|
validateIdentifier(name, "alias");
|
|
12687
13010
|
return `"${name.replace(/"/g, '""')}"`;
|
|
12688
13011
|
}
|
|
12689
|
-
function qualifyTable2(tableName, schemaName, naming) {
|
|
13012
|
+
function qualifyTable2(tableName, schemaName, naming, transformSchema = true) {
|
|
12690
13013
|
const table = quoteIdentifier(naming.toDatabase(tableName));
|
|
12691
13014
|
if (schemaName) {
|
|
12692
|
-
|
|
13015
|
+
const schema = transformSchema ? naming.toDatabase(schemaName) : schemaName;
|
|
13016
|
+
return `${quoteIdentifier(schema)}.${table}`;
|
|
12693
13017
|
}
|
|
12694
13018
|
return table;
|
|
12695
13019
|
}
|
|
@@ -12760,7 +13084,7 @@ function generateAlterTableAddFK(tableName, fk, schemaName, naming) {
|
|
|
12760
13084
|
`fk_${tableName}_${fk.columns.join("_")}`
|
|
12761
13085
|
);
|
|
12762
13086
|
const fkCols = fk.columns.map((col) => quoteIdentifier(naming.toDatabase(col))).join(", ");
|
|
12763
|
-
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);
|
|
12764
13088
|
const refCols = fk.references.columns.map((col) => quoteIdentifier(naming.toDatabase(col))).join(", ");
|
|
12765
13089
|
const onDelete = fk.onDelete ? ` ON DELETE ${mapOnDeleteAction(fk.onDelete)}` : "";
|
|
12766
13090
|
const onUpdate = fk.onUpdate ? ` ON UPDATE ${mapOnDeleteAction(fk.onUpdate)}` : "";
|
|
@@ -12774,8 +13098,9 @@ function generateCreateIndex(tableName, idx, schemaName, naming) {
|
|
|
12774
13098
|
);
|
|
12775
13099
|
const qualifiedTable = qualifyTable2(tableName, schemaName, naming);
|
|
12776
13100
|
const unique = idx.unique ? "UNIQUE " : "";
|
|
12777
|
-
|
|
12778
|
-
|
|
13101
|
+
const indexMethod = idx.method;
|
|
13102
|
+
if (indexMethod) validateIndexMethod(indexMethod, "index method");
|
|
13103
|
+
const method = indexMethod ? ` USING ${indexMethod}` : "";
|
|
12779
13104
|
const colParts = [];
|
|
12780
13105
|
if (idx.expressions && idx.expressions.length > 0) {
|
|
12781
13106
|
for (const expr of idx.expressions) {
|
|
@@ -12795,10 +13120,11 @@ function generateCreateIndex(tableName, idx, schemaName, naming) {
|
|
|
12795
13120
|
const nullsNotDistinct = idx.unique && idx.nullsNotDistinct ? " NULLS NOT DISTINCT" : "";
|
|
12796
13121
|
const withParams = idx.with && Object.keys(idx.with).length > 0 ? ` WITH (${Object.entries(idx.with).map(([k, v]) => {
|
|
12797
13122
|
validateIdentifier(k, "alias");
|
|
12798
|
-
return `${k} = ${v}`;
|
|
13123
|
+
return `${k} = ${formatStorageParameterValue(v, `index WITH parameter "${k}"`)}`;
|
|
12799
13124
|
}).join(", ")})` : "";
|
|
12800
|
-
|
|
12801
|
-
|
|
13125
|
+
const whereExpr = idx.where;
|
|
13126
|
+
if (whereExpr) validateSqlExpression(whereExpr, "index WHERE predicate");
|
|
13127
|
+
const where = whereExpr ? ` WHERE ${whereExpr}` : "";
|
|
12802
13128
|
return `CREATE ${unique}INDEX ${indexName} ON ${qualifiedTable}${method} (${cols})${include}${nullsNotDistinct}${withParams}${where};`;
|
|
12803
13129
|
}
|
|
12804
13130
|
function generateCreatePolicy(tableName, policy, schemaName, naming) {
|
|
@@ -13165,7 +13491,8 @@ function normalizeTable(table, plugin) {
|
|
|
13165
13491
|
columns: fk.columns.map(toDb),
|
|
13166
13492
|
references: {
|
|
13167
13493
|
table: toDb(fk.references.table),
|
|
13168
|
-
columns: fk.references.columns.map(toDb)
|
|
13494
|
+
columns: fk.references.columns.map(toDb),
|
|
13495
|
+
...fk.references.schema !== void 0 ? { schema: fk.references.schema } : {}
|
|
13169
13496
|
}
|
|
13170
13497
|
})),
|
|
13171
13498
|
indexes: table.indexes.map((idx) => ({
|
|
@@ -13258,6 +13585,21 @@ function compareColumnDetails(tableName, schema, db, changes) {
|
|
|
13258
13585
|
meta: { default: schema.default, oldDefault: db.default }
|
|
13259
13586
|
});
|
|
13260
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
|
+
}
|
|
13261
13603
|
if ((schema.collation ?? null) !== (db.collation ?? null)) {
|
|
13262
13604
|
changes.push({
|
|
13263
13605
|
kind: "alter_column_collation",
|
|
@@ -13329,7 +13671,8 @@ function comparePrimaryKeys(schema, db, changes) {
|
|
|
13329
13671
|
kind: "drop_primary_key",
|
|
13330
13672
|
table: schema.name,
|
|
13331
13673
|
destructive: true,
|
|
13332
|
-
details: `Drop primary key (${dbPK.join(", ")})
|
|
13674
|
+
details: `Drop primary key (${dbPK.join(", ")})`,
|
|
13675
|
+
meta: { columns: dbPK }
|
|
13333
13676
|
});
|
|
13334
13677
|
return;
|
|
13335
13678
|
}
|
|
@@ -13337,7 +13680,8 @@ function comparePrimaryKeys(schema, db, changes) {
|
|
|
13337
13680
|
kind: "drop_primary_key",
|
|
13338
13681
|
table: schema.name,
|
|
13339
13682
|
destructive: true,
|
|
13340
|
-
details: `Drop primary key (${dbPK.join(", ")})
|
|
13683
|
+
details: `Drop primary key (${dbPK.join(", ")})`,
|
|
13684
|
+
meta: { columns: dbPK }
|
|
13341
13685
|
});
|
|
13342
13686
|
changes.push({
|
|
13343
13687
|
kind: "add_primary_key",
|
|
@@ -13409,7 +13753,7 @@ function compareForeignKeys(schema, db, changes) {
|
|
|
13409
13753
|
}
|
|
13410
13754
|
}
|
|
13411
13755
|
function fkKey(fk) {
|
|
13412
|
-
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(",")})`;
|
|
13413
13757
|
}
|
|
13414
13758
|
function compareIndexes(schema, db, changes) {
|
|
13415
13759
|
const explicitIndexCols = new Set(
|
|
@@ -13442,16 +13786,15 @@ function compareIndexes(schema, db, changes) {
|
|
|
13442
13786
|
});
|
|
13443
13787
|
}
|
|
13444
13788
|
}
|
|
13445
|
-
const
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
)
|
|
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)
|
|
13452
13795
|
);
|
|
13453
13796
|
for (const [key, idx] of dbIdxMap) {
|
|
13454
|
-
if (!schemaIdxMap.has(key) && !autoFkIndexKeys.has(key) && !
|
|
13797
|
+
if (!schemaIdxMap.has(key) && !autoFkIndexKeys.has(key) && !isAutoUniqueIndex(schema.name, idx, autoUniqueIndexColumns)) {
|
|
13455
13798
|
changes.push({
|
|
13456
13799
|
kind: "drop_index",
|
|
13457
13800
|
table: schema.name,
|
|
@@ -13462,6 +13805,16 @@ function compareIndexes(schema, db, changes) {
|
|
|
13462
13805
|
}
|
|
13463
13806
|
}
|
|
13464
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
|
+
}
|
|
13465
13818
|
function indexKey(idx) {
|
|
13466
13819
|
const parts = [
|
|
13467
13820
|
idx.columns.join(","),
|
|
@@ -13826,6 +14179,7 @@ function buildSummary(changes) {
|
|
|
13826
14179
|
case "alter_column_type":
|
|
13827
14180
|
case "alter_column_nullable":
|
|
13828
14181
|
case "alter_column_default":
|
|
14182
|
+
case "alter_column_unique":
|
|
13829
14183
|
columns.altered++;
|
|
13830
14184
|
break;
|
|
13831
14185
|
case "add_primary_key":
|
|
@@ -14076,6 +14430,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14076
14430
|
pksResult,
|
|
14077
14431
|
fksResult,
|
|
14078
14432
|
indexesResult,
|
|
14433
|
+
uniqueColumnsResult,
|
|
14079
14434
|
enumsResult,
|
|
14080
14435
|
commentsResult,
|
|
14081
14436
|
checksResult,
|
|
@@ -14109,28 +14464,44 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14109
14464
|
// 3. Foreign keys
|
|
14110
14465
|
pool.query(
|
|
14111
14466
|
`SELECT
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
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`,
|
|
14134
14505
|
[schema]
|
|
14135
14506
|
),
|
|
14136
14507
|
// 4. Indexes (excluding PK-backing indexes and unique-constraint-backing indexes).
|
|
@@ -14194,7 +14565,19 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14194
14565
|
ORDER BY t.relname, i.relname`,
|
|
14195
14566
|
[schema]
|
|
14196
14567
|
),
|
|
14197
|
-
// 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
|
|
14198
14581
|
pool.query(
|
|
14199
14582
|
`SELECT
|
|
14200
14583
|
t.typname AS name,
|
|
@@ -14207,7 +14590,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14207
14590
|
GROUP BY t.typname`,
|
|
14208
14591
|
[schema]
|
|
14209
14592
|
),
|
|
14210
|
-
//
|
|
14593
|
+
// 7. Comments (table and column level) from pg_description
|
|
14211
14594
|
pool.query(
|
|
14212
14595
|
`SELECT
|
|
14213
14596
|
c.relname AS table_name,
|
|
@@ -14221,7 +14604,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14221
14604
|
AND d.objsubid >= 0`,
|
|
14222
14605
|
[schema]
|
|
14223
14606
|
),
|
|
14224
|
-
//
|
|
14607
|
+
// 8. CHECK constraints
|
|
14225
14608
|
pool.query(
|
|
14226
14609
|
`SELECT
|
|
14227
14610
|
c.conname AS name,
|
|
@@ -14233,7 +14616,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14233
14616
|
AND n.nspname = $1`,
|
|
14234
14617
|
[schema]
|
|
14235
14618
|
),
|
|
14236
|
-
//
|
|
14619
|
+
// 9. Partition configurations
|
|
14237
14620
|
pool.query(
|
|
14238
14621
|
`SELECT
|
|
14239
14622
|
c.relname AS table_name,
|
|
@@ -14248,13 +14631,13 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14248
14631
|
GROUP BY c.relname, p.partstrat`,
|
|
14249
14632
|
[schema]
|
|
14250
14633
|
),
|
|
14251
|
-
//
|
|
14634
|
+
// 10. Installed extensions (no schema param — queries globally; skip plpgsql)
|
|
14252
14635
|
pool.query(
|
|
14253
14636
|
`SELECT extname AS name
|
|
14254
14637
|
FROM pg_extension
|
|
14255
14638
|
WHERE extname != 'plpgsql'`
|
|
14256
14639
|
),
|
|
14257
|
-
//
|
|
14640
|
+
// 11. Sequences not backed by SERIAL
|
|
14258
14641
|
pool.query(
|
|
14259
14642
|
`SELECT s.sequencename AS name, s.start_value, s.increment_by, s.min_value, s.max_value, s.cycle
|
|
14260
14643
|
FROM pg_sequences s
|
|
@@ -14265,7 +14648,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14265
14648
|
AND d.objid IS NULL`,
|
|
14266
14649
|
[schema]
|
|
14267
14650
|
),
|
|
14268
|
-
//
|
|
14651
|
+
// 12. RLS enabled state per table
|
|
14269
14652
|
pool.query(
|
|
14270
14653
|
`SELECT c.relname AS table_name, c.relrowsecurity AS rls_enabled
|
|
14271
14654
|
FROM pg_class c
|
|
@@ -14274,7 +14657,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14274
14657
|
AND c.relkind = 'r'`,
|
|
14275
14658
|
[schema]
|
|
14276
14659
|
),
|
|
14277
|
-
//
|
|
14660
|
+
// 13. Row-Level Security policies
|
|
14278
14661
|
pool.query(
|
|
14279
14662
|
`SELECT
|
|
14280
14663
|
c.relname AS table_name,
|
|
@@ -14296,6 +14679,7 @@ async function queryAllCatalogs(pool, schema) {
|
|
|
14296
14679
|
pks: pksResult.rows,
|
|
14297
14680
|
fks: fksResult.rows,
|
|
14298
14681
|
indexes: indexesResult.rows,
|
|
14682
|
+
uniqueColumns: uniqueColumnsResult.rows,
|
|
14299
14683
|
enums: enumsResult.rows,
|
|
14300
14684
|
comments: commentsResult.rows,
|
|
14301
14685
|
checks: checksResult.rows,
|
|
@@ -14381,6 +14765,21 @@ function buildIndexMap(rows) {
|
|
|
14381
14765
|
}
|
|
14382
14766
|
return result;
|
|
14383
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
|
+
}
|
|
14384
14783
|
function buildColumnMap(rows) {
|
|
14385
14784
|
const result = /* @__PURE__ */ new Map();
|
|
14386
14785
|
for (const col of rows) {
|
|
@@ -14408,14 +14807,16 @@ function buildPKMap(rows) {
|
|
|
14408
14807
|
function buildFKMap(rows) {
|
|
14409
14808
|
const result = /* @__PURE__ */ new Map();
|
|
14410
14809
|
for (const fk of rows) {
|
|
14411
|
-
const
|
|
14810
|
+
const key = JSON.stringify([fk.source_table, fk.constraint_name]);
|
|
14811
|
+
const existing = result.get(key);
|
|
14412
14812
|
if (existing) {
|
|
14413
14813
|
existing.cols.push(fk.source_column);
|
|
14414
14814
|
existing.refs.push(fk.target_column);
|
|
14415
14815
|
} else {
|
|
14416
|
-
result.set(
|
|
14816
|
+
result.set(key, {
|
|
14417
14817
|
source: fk.source_table,
|
|
14418
14818
|
target: fk.target_table,
|
|
14819
|
+
targetSchema: fk.target_schema,
|
|
14419
14820
|
cols: [fk.source_column],
|
|
14420
14821
|
refs: [fk.target_column],
|
|
14421
14822
|
deleteRule: fk.delete_rule,
|
|
@@ -14481,10 +14882,12 @@ function buildRLSMaps(rlsRows, policiesRows) {
|
|
|
14481
14882
|
function buildTableIR(tableName, ctx) {
|
|
14482
14883
|
const rawCols = ctx.tableColumns.get(tableName) ?? [];
|
|
14483
14884
|
const pkCols = ctx.tablePKs.get(tableName);
|
|
14885
|
+
const uniqueColumns = ctx.uniqueColumns.get(tableName);
|
|
14484
14886
|
const columns = rawCols.map((col) => {
|
|
14485
14887
|
const identity = col.is_identity === "YES" && col.identity_generation ? col.identity_generation === "ALWAYS" ? "always" : "byDefault" : void 0;
|
|
14486
14888
|
const collation = col.collation_name && col.collation_name !== "default" ? col.collation_name : void 0;
|
|
14487
14889
|
const colComment = ctx.columnComments.get(`${tableName}.${col.column_name}`) ?? void 0;
|
|
14890
|
+
const uniqueConstraintName = uniqueColumns?.get(col.column_name);
|
|
14488
14891
|
return {
|
|
14489
14892
|
name: col.column_name,
|
|
14490
14893
|
type: mapPgType(col.data_type, col.udt_name),
|
|
@@ -14494,6 +14897,7 @@ function buildTableIR(tableName, ctx) {
|
|
|
14494
14897
|
// of quoting it as a string literal.
|
|
14495
14898
|
...col.column_default != null ? { default: { sql: col.column_default } } : {},
|
|
14496
14899
|
originalDbType: col.udt_name,
|
|
14900
|
+
...uniqueConstraintName !== void 0 ? { unique: true, uniqueConstraintName } : {},
|
|
14497
14901
|
...collation ? { collation } : {},
|
|
14498
14902
|
...identity ? { identity } : {},
|
|
14499
14903
|
...colComment ? { comment: colComment } : {}
|
|
@@ -14502,12 +14906,17 @@ function buildTableIR(tableName, ctx) {
|
|
|
14502
14906
|
const foreignKeys = [];
|
|
14503
14907
|
for (const [, fk] of ctx.fksByConstraint) {
|
|
14504
14908
|
if (fk.source !== tableName) continue;
|
|
14505
|
-
|
|
14909
|
+
const isCrossSchema = fk.targetSchema !== void 0 && fk.targetSchema !== ctx.schema;
|
|
14910
|
+
if (!isCrossSchema && !ctx.tableNames.includes(fk.target)) continue;
|
|
14506
14911
|
const onDelete = mapDeleteRule(fk.deleteRule);
|
|
14507
14912
|
const onUpdate = mapDeleteRule(fk.updateRule);
|
|
14508
14913
|
foreignKeys.push({
|
|
14509
14914
|
columns: fk.cols,
|
|
14510
|
-
references: {
|
|
14915
|
+
references: {
|
|
14916
|
+
table: fk.target,
|
|
14917
|
+
columns: fk.refs,
|
|
14918
|
+
...isCrossSchema ? { schema: fk.targetSchema } : {}
|
|
14919
|
+
},
|
|
14511
14920
|
...onDelete !== "NO ACTION" ? { onDelete } : {},
|
|
14512
14921
|
...onUpdate !== "NO ACTION" ? { onUpdate } : {},
|
|
14513
14922
|
...fk.deferred ? { deferred: true } : {}
|
|
@@ -14555,6 +14964,7 @@ async function introspect(pool, options) {
|
|
|
14555
14964
|
const tablePartitions = buildPartitionMap(raw.partitions);
|
|
14556
14965
|
const tableChecks = buildCheckMap(raw.checks);
|
|
14557
14966
|
const tableIndexes = buildIndexMap(raw.indexes);
|
|
14967
|
+
const uniqueColumns = buildUniqueColumnMap(raw.uniqueColumns);
|
|
14558
14968
|
const tableColumns = buildColumnMap(raw.columns);
|
|
14559
14969
|
const tablePKs = buildPKMap(raw.pks);
|
|
14560
14970
|
const fksByConstraint = buildFKMap(raw.fks);
|
|
@@ -14573,6 +14983,7 @@ async function introspect(pool, options) {
|
|
|
14573
14983
|
tablePKs,
|
|
14574
14984
|
fksByConstraint,
|
|
14575
14985
|
tableIndexes,
|
|
14986
|
+
uniqueColumns,
|
|
14576
14987
|
tableChecks,
|
|
14577
14988
|
tableComments,
|
|
14578
14989
|
columnComments,
|
|
@@ -14580,20 +14991,33 @@ async function introspect(pool, options) {
|
|
|
14580
14991
|
tableRlsEnabled,
|
|
14581
14992
|
tablePolicies,
|
|
14582
14993
|
tableNames,
|
|
14994
|
+
schema,
|
|
14583
14995
|
warnings
|
|
14584
14996
|
});
|
|
14585
14997
|
tables.set(tableName, table);
|
|
14586
14998
|
}
|
|
14587
14999
|
const extensions = raw.extensions.map((r) => r.name);
|
|
14588
15000
|
const sequenceMap = buildSequenceMap(raw.sequences);
|
|
14589
|
-
const relations = inferRelations(fksByConstraint, tableNames);
|
|
14590
|
-
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
|
+
}
|
|
14591
15014
|
const modelIR = new ModelIRImpl(
|
|
14592
15015
|
tables,
|
|
14593
15016
|
relations,
|
|
14594
15017
|
enumMap,
|
|
14595
15018
|
extensions,
|
|
14596
|
-
sequenceMap
|
|
15019
|
+
sequenceMap,
|
|
15020
|
+
externalTables
|
|
14597
15021
|
);
|
|
14598
15022
|
return Object.assign(modelIR, {
|
|
14599
15023
|
hierarchies,
|
|
@@ -14601,10 +15025,11 @@ async function introspect(pool, options) {
|
|
|
14601
15025
|
warnings
|
|
14602
15026
|
});
|
|
14603
15027
|
}
|
|
14604
|
-
function inferRelations(fksByConstraint, filteredTables) {
|
|
15028
|
+
function inferRelations(fksByConstraint, filteredTables, schema) {
|
|
14605
15029
|
const relations = /* @__PURE__ */ new Map();
|
|
14606
15030
|
const filteredSet = new Set(filteredTables);
|
|
14607
15031
|
for (const [, fk] of fksByConstraint) {
|
|
15032
|
+
if (fk.targetSchema !== void 0 && fk.targetSchema !== schema) continue;
|
|
14608
15033
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
14609
15034
|
const belongsToName = deriveRelationName(fk.cols[0], fk.target);
|
|
14610
15035
|
const relationFk = {
|
|
@@ -14654,13 +15079,14 @@ function deriveRelationName(fkColumn, _targetTable) {
|
|
|
14654
15079
|
}
|
|
14655
15080
|
return fkColumn;
|
|
14656
15081
|
}
|
|
14657
|
-
function detectHierarchies(tables, fksByConstraint, filteredTables) {
|
|
15082
|
+
function detectHierarchies(tables, fksByConstraint, filteredTables, schema) {
|
|
14658
15083
|
const hierarchies = [];
|
|
14659
15084
|
const filteredSet = new Set(filteredTables);
|
|
14660
15085
|
const fksBySourceTarget = /* @__PURE__ */ new Map();
|
|
14661
15086
|
for (const [, fk] of fksByConstraint) {
|
|
15087
|
+
if (fk.targetSchema !== void 0 && fk.targetSchema !== schema) continue;
|
|
14662
15088
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
14663
|
-
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)) {
|
|
14664
15090
|
const table = tables.get(fk.source);
|
|
14665
15091
|
const pk = table?.primaryKey;
|
|
14666
15092
|
const nodeIdColumn = typeof pk === "string" ? pk : pk?.[0] ?? DEFAULT_PK_COLUMN;
|
|
@@ -16935,14 +17361,16 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
|
|
|
16935
17361
|
schemaForFromName(schemaName, intent.table, deps.bindingNames, naming),
|
|
16936
17362
|
naming
|
|
16937
17363
|
);
|
|
16938
|
-
|
|
17364
|
+
const joinDecision = {
|
|
16939
17365
|
type: "join",
|
|
16940
17366
|
targetTable: intent.table,
|
|
16941
17367
|
alias: tableAlias,
|
|
16942
17368
|
joinType: intent.type,
|
|
16943
17369
|
joinRarg: joinedRangeVar,
|
|
16944
|
-
joinOnNode: onNode
|
|
16945
|
-
|
|
17370
|
+
joinOnNode: onNode,
|
|
17371
|
+
joinOnParams: paramState.parameters
|
|
17372
|
+
};
|
|
17373
|
+
results.push(joinDecision);
|
|
16946
17374
|
}
|
|
16947
17375
|
}
|
|
16948
17376
|
return results;
|
|
@@ -18149,8 +18577,9 @@ function qualifyTable3(table, schema) {
|
|
|
18149
18577
|
return schema ? `${quoteIdent2(schema, "schema")}.${quoteIdent2(table, "table")}` : quoteIdent2(table, "table");
|
|
18150
18578
|
}
|
|
18151
18579
|
function generateCreateIndexSQL(table, options, schema) {
|
|
18152
|
-
|
|
18153
|
-
|
|
18580
|
+
const indexMethod = options.method;
|
|
18581
|
+
if (indexMethod !== void 0) {
|
|
18582
|
+
validateIndexMethod(indexMethod, "index method");
|
|
18154
18583
|
}
|
|
18155
18584
|
const parts = ["CREATE"];
|
|
18156
18585
|
if (options.unique) parts.push("UNIQUE");
|
|
@@ -18159,8 +18588,8 @@ function generateCreateIndexSQL(table, options, schema) {
|
|
|
18159
18588
|
if (options.ifNotExists) parts.push("IF NOT EXISTS");
|
|
18160
18589
|
parts.push(quoteIdentifier2(options.name));
|
|
18161
18590
|
parts.push(`ON ${qualifyTable3(table, schema)}`);
|
|
18162
|
-
if (
|
|
18163
|
-
parts.push(`USING ${
|
|
18591
|
+
if (indexMethod) {
|
|
18592
|
+
parts.push(`USING ${indexMethod}`);
|
|
18164
18593
|
}
|
|
18165
18594
|
const colParts = [];
|
|
18166
18595
|
for (const col of options.columns) {
|
|
@@ -18177,13 +18606,14 @@ function generateCreateIndexSQL(table, options, schema) {
|
|
|
18177
18606
|
if (options.with && Object.keys(options.with).length > 0) {
|
|
18178
18607
|
const withParams = Object.entries(options.with).map(([k, v]) => {
|
|
18179
18608
|
validateIdentifier(k, "alias");
|
|
18180
|
-
return `${k} = ${v}`;
|
|
18609
|
+
return `${k} = ${formatStorageParameterValue(v, `index WITH parameter "${k}"`)}`;
|
|
18181
18610
|
}).join(", ");
|
|
18182
18611
|
parts.push(`WITH (${withParams})`);
|
|
18183
18612
|
}
|
|
18184
|
-
|
|
18185
|
-
|
|
18186
|
-
|
|
18613
|
+
const whereExpr = options.where;
|
|
18614
|
+
if (whereExpr) {
|
|
18615
|
+
validateSqlExpression(whereExpr, "index WHERE predicate");
|
|
18616
|
+
parts.push(`WHERE ${whereExpr}`);
|
|
18187
18617
|
}
|
|
18188
18618
|
return parts.join(" ");
|
|
18189
18619
|
}
|
|
@@ -18194,10 +18624,11 @@ function buildColumnPart(col, opclassMap) {
|
|
|
18194
18624
|
if (opclass2) validateIdentifier(opclass2, "alias");
|
|
18195
18625
|
return opclass2 ? `${quoted} ${opclass2}` : quoted;
|
|
18196
18626
|
}
|
|
18197
|
-
|
|
18627
|
+
const expression = col.expression;
|
|
18628
|
+
validateSqlExpression(expression, "index expression");
|
|
18198
18629
|
const opclass = col.opclass;
|
|
18199
18630
|
if (opclass) validateIdentifier(opclass, "alias");
|
|
18200
|
-
return opclass ? `${
|
|
18631
|
+
return opclass ? `${expression} ${opclass}` : expression;
|
|
18201
18632
|
}
|
|
18202
18633
|
function generateDropIndexSQL(name, options) {
|
|
18203
18634
|
const parts = ["DROP INDEX"];
|