@vibeorm/sql 2.0.0-alpha.6 → 2.0.0-alpha.8
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/ast.d.ts +108 -2
- package/dist/ast.d.ts.map +1 -1
- package/dist/capabilities.d.ts +2 -12
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/codecs.d.ts +16 -3
- package/dist/codecs.d.ts.map +1 -1
- package/dist/dialect.d.ts +63 -1
- package/dist/dialect.d.ts.map +1 -1
- package/dist/dialects/diagnostics.d.ts +43 -0
- package/dist/dialects/diagnostics.d.ts.map +1 -0
- package/dist/dialects/mysql.d.ts.map +1 -1
- package/dist/dialects/postgres-diagnostics.d.ts +67 -0
- package/dist/dialects/postgres-diagnostics.d.ts.map +1 -0
- package/dist/dialects/postgres-migration.d.ts +83 -0
- package/dist/dialects/postgres-migration.d.ts.map +1 -0
- package/dist/dialects/postgres-rls-readiness.d.ts +38 -0
- package/dist/dialects/postgres-rls-readiness.d.ts.map +1 -0
- package/dist/dialects/postgres-rls.d.ts +71 -0
- package/dist/dialects/postgres-rls.d.ts.map +1 -0
- package/dist/dialects/postgres.d.ts.map +1 -1
- package/dist/dialects/sqlite.d.ts.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +725 -81
- package/dist/index.js.map +16 -11
- package/dist/render.d.ts +3 -9
- package/dist/render.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -42,64 +42,33 @@ function jsonParamCell(params) {
|
|
|
42
42
|
function isJsonParamCell(value) {
|
|
43
43
|
return typeof value === "object" && value !== null && value.__jsonParam === true;
|
|
44
44
|
}
|
|
45
|
+
function excludedRef(params) {
|
|
46
|
+
return { __excluded: true, column: params.column };
|
|
47
|
+
}
|
|
48
|
+
function isExcludedRef(value) {
|
|
49
|
+
return typeof value === "object" && value !== null && value.__excluded === true;
|
|
50
|
+
}
|
|
45
51
|
// packages/sql/src/capabilities.ts
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
distinctOn: true,
|
|
53
|
-
partialIndexes: true,
|
|
54
|
-
conflictTargetConstraint: true,
|
|
55
|
-
transactionalDdl: true,
|
|
56
|
-
createManyAndReturn: true,
|
|
57
|
-
jsonColumnType: true,
|
|
58
|
-
orderByNulls: true,
|
|
59
|
-
fullTextEngines: ["bm25", "tsvector"]
|
|
60
|
-
};
|
|
61
|
-
var SQLITE_CAPABILITIES = {
|
|
62
|
-
returning: true,
|
|
63
|
-
scalarArrays: false,
|
|
64
|
-
nativeEnums: false,
|
|
65
|
-
ilike: false,
|
|
66
|
-
lateralJoin: false,
|
|
67
|
-
distinctOn: false,
|
|
68
|
-
partialIndexes: true,
|
|
69
|
-
conflictTargetConstraint: false,
|
|
70
|
-
transactionalDdl: true,
|
|
71
|
-
createManyAndReturn: true,
|
|
72
|
-
jsonColumnType: false,
|
|
73
|
-
orderByNulls: true,
|
|
74
|
-
fullTextEngines: ["fts5"]
|
|
75
|
-
};
|
|
76
|
-
var MYSQL_CAPABILITIES = {
|
|
77
|
-
returning: false,
|
|
78
|
-
scalarArrays: false,
|
|
79
|
-
nativeEnums: true,
|
|
80
|
-
ilike: false,
|
|
81
|
-
lateralJoin: false,
|
|
82
|
-
distinctOn: false,
|
|
83
|
-
partialIndexes: false,
|
|
84
|
-
conflictTargetConstraint: false,
|
|
85
|
-
transactionalDdl: false,
|
|
86
|
-
createManyAndReturn: false,
|
|
87
|
-
jsonColumnType: true,
|
|
88
|
-
orderByNulls: false,
|
|
89
|
-
fullTextEngines: ["fulltext"]
|
|
90
|
-
};
|
|
91
|
-
var DIALECT_CAPABILITIES = {
|
|
92
|
-
postgres: POSTGRES_CAPABILITIES,
|
|
93
|
-
sqlite: SQLITE_CAPABILITIES,
|
|
94
|
-
mysql: MYSQL_CAPABILITIES
|
|
95
|
-
};
|
|
52
|
+
import {
|
|
53
|
+
DIALECT_CAPABILITIES,
|
|
54
|
+
POSTGRES_CAPABILITIES,
|
|
55
|
+
SQLITE_CAPABILITIES,
|
|
56
|
+
MYSQL_CAPABILITIES
|
|
57
|
+
} from "@vibeorm/schema";
|
|
96
58
|
|
|
97
59
|
// packages/sql/src/dialects/postgres.ts
|
|
98
60
|
var postgresDialect = {
|
|
99
61
|
name: "postgres",
|
|
100
62
|
capabilities: POSTGRES_CAPABILITIES,
|
|
101
63
|
inArrayStrategy: "any",
|
|
64
|
+
tupleInStrategy: "rowValue",
|
|
102
65
|
upsertForm: "onConflict",
|
|
66
|
+
conflictKeyEquality: {
|
|
67
|
+
foldingScalars: ["Decimal"],
|
|
68
|
+
textIsBinaryByDefault: true,
|
|
69
|
+
duplicatesInOneStatement: "reject"
|
|
70
|
+
},
|
|
71
|
+
excludedRowRef: { form: "keyword", name: "excluded" },
|
|
103
72
|
doNothingForm: "onConflictDoNothing",
|
|
104
73
|
insensitiveLike: "ilike",
|
|
105
74
|
strictMatchStrategy: "native",
|
|
@@ -138,7 +107,14 @@ var sqliteDialect = {
|
|
|
138
107
|
name: "sqlite",
|
|
139
108
|
capabilities: SQLITE_CAPABILITIES,
|
|
140
109
|
inArrayStrategy: "expand",
|
|
110
|
+
tupleInStrategy: "rowValue",
|
|
141
111
|
upsertForm: "onConflict",
|
|
112
|
+
conflictKeyEquality: {
|
|
113
|
+
foldingScalars: [],
|
|
114
|
+
textIsBinaryByDefault: true,
|
|
115
|
+
duplicatesInOneStatement: "lastWriteWins"
|
|
116
|
+
},
|
|
117
|
+
excludedRowRef: { form: "keyword", name: "excluded" },
|
|
142
118
|
doNothingForm: "onConflictDoNothing",
|
|
143
119
|
insensitiveLike: "lower",
|
|
144
120
|
strictMatchStrategy: "glob",
|
|
@@ -177,7 +153,15 @@ var mysqlDialect = {
|
|
|
177
153
|
name: "mysql",
|
|
178
154
|
capabilities: MYSQL_CAPABILITIES,
|
|
179
155
|
inArrayStrategy: "expand",
|
|
156
|
+
tupleInStrategy: "rowValue",
|
|
180
157
|
upsertForm: "onDuplicateKey",
|
|
158
|
+
conflictKeyEquality: {
|
|
159
|
+
foldingScalars: ["Decimal"],
|
|
160
|
+
defaultStringMaxLength: 191,
|
|
161
|
+
textIsBinaryByDefault: false,
|
|
162
|
+
duplicatesInOneStatement: "lastWriteWins"
|
|
163
|
+
},
|
|
164
|
+
excludedRowRef: { form: "rowAlias", alias: "new" },
|
|
181
165
|
doNothingForm: "insertIgnore",
|
|
182
166
|
insensitiveLike: "lower",
|
|
183
167
|
strictMatchStrategy: "binary",
|
|
@@ -221,7 +205,27 @@ function getSqlDialect(params) {
|
|
|
221
205
|
return SQL_DIALECTS[params.dialect];
|
|
222
206
|
}
|
|
223
207
|
// packages/sql/src/codecs.ts
|
|
224
|
-
import { VibeError } from "@vibeorm/schema";
|
|
208
|
+
import { VibeError, invalidStoredData } from "@vibeorm/schema";
|
|
209
|
+
function enumMembershipCodec(params) {
|
|
210
|
+
const { model, field, enum: enumName } = params;
|
|
211
|
+
const members = new Set(params.members);
|
|
212
|
+
return {
|
|
213
|
+
encode: (value) => {
|
|
214
|
+
if (typeof value === "string" && members.has(value))
|
|
215
|
+
return value;
|
|
216
|
+
throw new VibeError({
|
|
217
|
+
code: "VIBE_VALIDATION",
|
|
218
|
+
message: `Invalid value for enum ${enumName} on ${model}.${field}`,
|
|
219
|
+
meta: { model, field, enum: enumName }
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
decode: (value) => {
|
|
223
|
+
if (typeof value === "string" && members.has(value))
|
|
224
|
+
return value;
|
|
225
|
+
throw invalidStoredData({ model, field, enum: enumName, value });
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
225
229
|
function decodeIsNoop(params) {
|
|
226
230
|
const { scalarType, wire } = params;
|
|
227
231
|
if (wire === undefined)
|
|
@@ -367,8 +371,435 @@ var DIALECT_CODECS = {
|
|
|
367
371
|
sqlite: SQLITE_CODECS,
|
|
368
372
|
mysql: MYSQL_CODECS
|
|
369
373
|
};
|
|
374
|
+
// packages/sql/src/dialects/diagnostics.ts
|
|
375
|
+
import { unsupportedCapability } from "@vibeorm/schema";
|
|
376
|
+
var QUERIES = Object.freeze({
|
|
377
|
+
postgresJsonWire: { dialect: "postgres", text: `SELECT '{"__vibeorm":1}'::jsonb AS "probe"` },
|
|
378
|
+
sqliteTriggers: { dialect: "sqlite", text: "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'trigger'" }
|
|
379
|
+
});
|
|
380
|
+
function renderDiagnostic(params) {
|
|
381
|
+
const query = QUERIES[params.query];
|
|
382
|
+
if (params.dialect.name !== query.dialect) {
|
|
383
|
+
throw unsupportedCapability({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
|
|
384
|
+
}
|
|
385
|
+
return { text: query.text, values: [], returning: "none" };
|
|
386
|
+
}
|
|
387
|
+
function isFixedDiagnostic(params) {
|
|
388
|
+
return params.query === "postgresJsonWire" || params.query === "sqliteTriggers";
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// packages/sql/src/dialects/postgres-diagnostics.ts
|
|
392
|
+
import { VibeError as VibeError2, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
|
|
393
|
+
|
|
394
|
+
// packages/sql/src/params.ts
|
|
395
|
+
class ParamCollector {
|
|
396
|
+
dialect;
|
|
397
|
+
values;
|
|
398
|
+
constructor(dialect) {
|
|
399
|
+
this.dialect = dialect;
|
|
400
|
+
this.values = [];
|
|
401
|
+
}
|
|
402
|
+
add(value) {
|
|
403
|
+
this.values.push(value);
|
|
404
|
+
return this.dialect.placeholder({ index: this.values.length });
|
|
405
|
+
}
|
|
406
|
+
addMany(values) {
|
|
407
|
+
return values.map((value) => this.add(value));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// packages/sql/src/dialects/postgres-diagnostics.ts
|
|
412
|
+
function renderPostgresExplain(params) {
|
|
413
|
+
const { dialect, query, options } = params;
|
|
414
|
+
requirePostgres({ dialect, feature: "query plan inspection (EXPLAIN)" });
|
|
415
|
+
const parts = [];
|
|
416
|
+
if (options?.analyze === true)
|
|
417
|
+
parts.push("ANALYZE");
|
|
418
|
+
parts.push("FORMAT JSON");
|
|
419
|
+
if (options?.verbose === true)
|
|
420
|
+
parts.push("VERBOSE");
|
|
421
|
+
if (options?.buffers === true)
|
|
422
|
+
parts.push("BUFFERS");
|
|
423
|
+
if (options?.settings === true)
|
|
424
|
+
parts.push("SETTINGS");
|
|
425
|
+
return { text: `EXPLAIN (${parts.join(", ")}) ${query.text}`, values: [...query.values], returning: "none" };
|
|
426
|
+
}
|
|
427
|
+
var POSTGRES_DIAGNOSTIC_SQL = Object.freeze({
|
|
428
|
+
planContext: `SELECT current_user::text AS "currentUser", session_user::text AS "sessionUser", r.rolsuper AS "superuser", r.rolbypassrls AS "bypassRls", pg_catalog.current_setting('server_version_num') AS "serverVersionNum", pg_catalog.current_database()::text AS "database" FROM pg_catalog.pg_roles r WHERE r.rolname = current_user`,
|
|
429
|
+
statementExtension: `SELECT (SELECT e.extversion FROM pg_catalog.pg_extension e WHERE e.extname = 'pg_stat_statements') AS "installedVersion", EXISTS (SELECT 1 FROM pg_catalog.pg_available_extensions a WHERE a.name = 'pg_stat_statements') AS "available", (SELECT n.nspname::text FROM pg_catalog.pg_extension e JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace WHERE e.extname = 'pg_stat_statements') AS "schema", pg_catalog.current_setting('server_version_num') AS "serverVersionNum", pg_catalog.pg_has_role(current_user, 'pg_read_all_stats', 'MEMBER') AS "readAllStats", current_user::text AS "currentUser"`,
|
|
430
|
+
statementInfo: `SELECT i.stats_reset::text AS "statsReset" FROM %SCHEMA%.pg_stat_statements_info i`,
|
|
431
|
+
statementSnapshot: `SELECT s.queryid::text AS "queryId", s.query AS "query", s.calls AS "calls", s.total_exec_time AS "totalExecTimeMs", s.mean_exec_time AS "meanExecTimeMs", s.min_exec_time AS "minExecTimeMs", s.max_exec_time AS "maxExecTimeMs", s.rows AS "rows", s.shared_blks_hit AS "sharedBlocksHit", s.shared_blks_read AS "sharedBlocksRead", s.temp_blks_read AS "tempBlocksRead", s.temp_blks_written AS "tempBlocksWritten" FROM %SCHEMA%.pg_stat_statements s JOIN pg_catalog.pg_database d ON d.oid = s.dbid WHERE d.datname = pg_catalog.current_database() ORDER BY s.total_exec_time DESC LIMIT %LIMIT%`,
|
|
432
|
+
indexes: `SELECT i.relname::text AS "index", pg_catalog.pg_get_indexdef(x.indexrelid) AS "definition", x.indisunique AS "unique", x.indisprimary AS "primary", x.indisvalid AS "valid", pg_catalog.pg_relation_size(x.indexrelid) AS "indexBytes" FROM pg_catalog.pg_index x JOIN pg_catalog.pg_class c ON c.oid = x.indrelid JOIN pg_catalog.pg_class i ON i.oid = x.indexrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = %SCHEMA_PARAM% AND c.relname = %TABLE_PARAM% ORDER BY i.relname`,
|
|
433
|
+
sessionBegin: "BEGIN",
|
|
434
|
+
sessionReadOnly: "SET TRANSACTION READ ONLY",
|
|
435
|
+
sessionCommit: "COMMIT",
|
|
436
|
+
sessionRollback: "ROLLBACK",
|
|
437
|
+
setContext: `SELECT pg_catalog.set_config(%KEY%, %VALUE%, true)`,
|
|
438
|
+
tableStats: `SELECT c.reltuples::float8 AS "estimatedRows", c.relpages::bigint AS "pages", pg_catalog.pg_table_size(c.oid) AS "tableBytes", c.relrowsecurity AS "rlsEnabled", c.relforcerowsecurity AS "rlsForced", (SELECT s.last_analyze::text FROM pg_catalog.pg_stat_all_tables s WHERE s.relid = c.oid) AS "lastAnalyze", (SELECT s.last_autoanalyze::text FROM pg_catalog.pg_stat_all_tables s WHERE s.relid = c.oid) AS "lastAutoAnalyze" FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = %SCHEMA_PARAM% AND c.relname = %TABLE_PARAM%`
|
|
439
|
+
});
|
|
440
|
+
var POSTGRES_WORKLOAD_LIMITS = Object.freeze({
|
|
441
|
+
minLimit: 1,
|
|
442
|
+
maxLimit: 1000
|
|
443
|
+
});
|
|
444
|
+
function requirePostgres(params) {
|
|
445
|
+
if (params.dialect.name !== "postgres") {
|
|
446
|
+
throw unsupportedCapability2({
|
|
447
|
+
dialect: params.dialect.name,
|
|
448
|
+
feature: params.feature,
|
|
449
|
+
hint: "plan inspection and workload statistics are PostgreSQL-shaped; portable operation tracing works on every dialect"
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function renderPostgresWorkload(params) {
|
|
454
|
+
const { dialect, query, schema, table, limit } = params;
|
|
455
|
+
requirePostgres({ dialect, feature: `the "${query}" diagnostic read` });
|
|
456
|
+
const collector = new ParamCollector(dialect);
|
|
457
|
+
let text = POSTGRES_DIAGNOSTIC_SQL[query];
|
|
458
|
+
if (query === "statementInfo" || query === "statementSnapshot") {
|
|
459
|
+
text = text.replace("%SCHEMA%", dialect.quoteIdent({ name: requireArg({ value: schema, name: "schema", query }) }));
|
|
460
|
+
}
|
|
461
|
+
if (query === "statementSnapshot") {
|
|
462
|
+
const bounded = limit ?? 20;
|
|
463
|
+
if (!Number.isInteger(bounded) || bounded < POSTGRES_WORKLOAD_LIMITS.minLimit || bounded > POSTGRES_WORKLOAD_LIMITS.maxLimit) {
|
|
464
|
+
throw new VibeError2({
|
|
465
|
+
code: "VIBE_CONFIG",
|
|
466
|
+
message: `workload snapshot limit must be an integer between ${POSTGRES_WORKLOAD_LIMITS.minLimit} and ${POSTGRES_WORKLOAD_LIMITS.maxLimit}, got ${String(limit)}`,
|
|
467
|
+
meta: { limit }
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
text = text.replace("%LIMIT%", collector.add(bounded));
|
|
471
|
+
}
|
|
472
|
+
if (query === "setContext") {
|
|
473
|
+
text = text.replace("%KEY%", collector.add(requireArg({ value: params.key, name: "key", query }))).replace("%VALUE%", collector.add(params.value ?? ""));
|
|
474
|
+
}
|
|
475
|
+
if (query === "indexes" || query === "tableStats") {
|
|
476
|
+
text = text.replace("%SCHEMA_PARAM%", collector.add(requireArg({ value: schema, name: "schema", query }))).replace("%TABLE_PARAM%", collector.add(requireArg({ value: table, name: "table", query })));
|
|
477
|
+
}
|
|
478
|
+
return { text, values: collector.values, returning: "none" };
|
|
479
|
+
}
|
|
480
|
+
function requireArg(params) {
|
|
481
|
+
const { value, name, query } = params;
|
|
482
|
+
if (value === undefined || value.length === 0) {
|
|
483
|
+
throw new VibeError2({
|
|
484
|
+
code: "VIBE_CONFIG",
|
|
485
|
+
message: `the "${query}" diagnostic read requires a ${name}`,
|
|
486
|
+
meta: { query, argument: name }
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
return value;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// packages/sql/src/dialects/postgres-rls-readiness.ts
|
|
493
|
+
import { unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
|
|
494
|
+
|
|
495
|
+
// packages/sql/src/dialects/postgres-rls.ts
|
|
496
|
+
import { RlsContextError, VibeError as VibeError3, assertValidSchemaIR, unsupportedCapability as unsupportedCapability3, validateRlsContextIR } from "@vibeorm/schema";
|
|
497
|
+
var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
|
|
498
|
+
var COLUMN_TYPES = {
|
|
499
|
+
String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
|
|
500
|
+
Boolean: { default: "boolean", native: { boolean: "boolean", bool: "boolean" } },
|
|
501
|
+
Int: { default: "integer", native: { integer: "integer", int: "integer", int4: "integer", smallint: "smallint", int2: "smallint" } },
|
|
502
|
+
BigInt: { default: "bigint", native: { bigint: "bigint", int8: "bigint" } },
|
|
503
|
+
Float: { default: "double precision", native: { doubleprecision: "double precision", float8: "double precision", real: "real", float4: "real" } },
|
|
504
|
+
Decimal: { default: "numeric", native: { numeric: "numeric", decimal: "numeric" } },
|
|
505
|
+
DateTime: { default: "timestamptz", native: { timestamptz: "timestamptz", timestampwithtimezone: "timestamptz", timestamp: "timestamp", timestampwithouttimezone: "timestamp", date: "date" } }
|
|
506
|
+
};
|
|
507
|
+
function requireRls(params) {
|
|
508
|
+
if (!params.dialect.capabilities.nativeRowLevelSecurity) {
|
|
509
|
+
throw unsupportedCapability3({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function identifier(params) {
|
|
513
|
+
if (typeof params.name !== "string" || params.name.length === 0 || params.name.includes("\x00") || new TextEncoder().encode(params.name).length > 63) {
|
|
514
|
+
throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
|
|
515
|
+
}
|
|
516
|
+
return params.dialect.quoteIdent({ name: params.name });
|
|
517
|
+
}
|
|
518
|
+
function tableRef(params) {
|
|
519
|
+
if (params.schema !== undefined && params.schema !== "public" && params.schema !== "pg_temp") {
|
|
520
|
+
throw unsupportedCapability3({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
|
|
521
|
+
}
|
|
522
|
+
return `${identifier({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier({ dialect: params.dialect, name: params.table })}`;
|
|
523
|
+
}
|
|
524
|
+
function query(params) {
|
|
525
|
+
return { text: params.text, values: params.values ?? [], returning: "none" };
|
|
526
|
+
}
|
|
527
|
+
function renderRlsEnable(params) {
|
|
528
|
+
requireRls(params);
|
|
529
|
+
return query({ text: `ALTER TABLE ${tableRef(params)} ENABLE ROW LEVEL SECURITY` });
|
|
530
|
+
}
|
|
531
|
+
function renderRlsForce(params) {
|
|
532
|
+
requireRls(params);
|
|
533
|
+
return query({ text: `ALTER TABLE ${tableRef(params)} FORCE ROW LEVEL SECURITY` });
|
|
534
|
+
}
|
|
535
|
+
function renderRlsDisable({ dialect, table }) {
|
|
536
|
+
requireRls({ dialect });
|
|
537
|
+
return query({ text: `ALTER TABLE ${tableRef({ dialect, table })} DISABLE ROW LEVEL SECURITY` });
|
|
538
|
+
}
|
|
539
|
+
function renderRlsNoForce({ dialect, table }) {
|
|
540
|
+
requireRls({ dialect });
|
|
541
|
+
return query({ text: `ALTER TABLE ${tableRef({ dialect, table })} NO FORCE ROW LEVEL SECURITY` });
|
|
542
|
+
}
|
|
543
|
+
function renderRlsDropPolicy({ dialect, table, policyName }) {
|
|
544
|
+
requireRls({ dialect });
|
|
545
|
+
const name = identifier({ dialect, name: policyName ?? NATIVE_RLS.policyName });
|
|
546
|
+
return query({ text: `DROP POLICY ${name} ON ${tableRef({ dialect, table })}` });
|
|
547
|
+
}
|
|
548
|
+
function renderRlsColumnType(params) {
|
|
549
|
+
requireRls(params);
|
|
550
|
+
const { field } = params;
|
|
551
|
+
const mapping = typeof field.type === "string" && Object.hasOwn(COLUMN_TYPES, field.type) ? COLUMN_TYPES[field.type] : undefined;
|
|
552
|
+
if (mapping !== undefined && !field.isList && !field.isUnsupported) {
|
|
553
|
+
if (field.nativeType === undefined)
|
|
554
|
+
return mapping.default;
|
|
555
|
+
const match = /^([a-z][a-z0-9 ]*)(?:\(\s*\d+(?:\s*,\s*\d+)?\s*\))?$/i.exec(field.nativeType.trim());
|
|
556
|
+
const base = match?.[1]?.toLowerCase().replace(/\s+/g, "");
|
|
557
|
+
const native = base !== undefined && Object.hasOwn(mapping.native, base) ? mapping.native[base] : undefined;
|
|
558
|
+
if (native !== undefined)
|
|
559
|
+
return native;
|
|
560
|
+
}
|
|
561
|
+
throw unsupportedCapability3({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
|
|
562
|
+
}
|
|
563
|
+
function protectedModel(params) {
|
|
564
|
+
requireRls(params);
|
|
565
|
+
assertValidSchemaIR({ schema: params.schema });
|
|
566
|
+
const model = params.schema.models.find((entry) => entry.name === params.model);
|
|
567
|
+
if (model === undefined || model.rls === undefined || model.rls === "unprotected") {
|
|
568
|
+
throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
|
|
569
|
+
}
|
|
570
|
+
return model;
|
|
571
|
+
}
|
|
572
|
+
function renderRlsPredicate(params) {
|
|
573
|
+
const model = protectedModel(params);
|
|
574
|
+
return predicate({ dialect: params.dialect, schema: params.schema, model });
|
|
575
|
+
}
|
|
576
|
+
function predicate(params) {
|
|
577
|
+
const { dialect, schema, model } = params;
|
|
578
|
+
const rule = model.rls;
|
|
579
|
+
if (rule === "deny")
|
|
580
|
+
return "false";
|
|
581
|
+
if (typeof rule === "object" && "where" in rule) {
|
|
582
|
+
return Object.entries(rule.where).map(([name, ref]) => {
|
|
583
|
+
const field = model.fields.find((entry) => entry.name === name);
|
|
584
|
+
const column = identifier({ dialect, name: field.dbName ?? field.name });
|
|
585
|
+
const physicalType = renderRlsColumnType({ dialect, field });
|
|
586
|
+
return `(${column} = NULLIF(current_setting('${NATIVE_RLS.settingPrefix}${ref.context}', true), '')::${physicalType})`;
|
|
587
|
+
}).join(" AND ");
|
|
588
|
+
}
|
|
589
|
+
if (typeof rule === "object" && "through" in rule) {
|
|
590
|
+
const relation = model.relations.find((entry) => entry.name === rule.through);
|
|
591
|
+
const parent = schema.models.find((entry) => entry.name === relation.targetModel);
|
|
592
|
+
const alias = identifier({ dialect, name: NATIVE_RLS.parentAlias });
|
|
593
|
+
const child = identifier({ dialect, name: model.dbName ?? model.name });
|
|
594
|
+
const join = relation.fields.map((name, index) => {
|
|
595
|
+
const childField = model.fields.find((field) => field.name === name);
|
|
596
|
+
const parentField = parent.fields.find((field) => field.name === relation.references[index]);
|
|
597
|
+
return `(${alias}.${identifier({ dialect, name: parentField.dbName ?? parentField.name })} = ${child}.${identifier({ dialect, name: childField.dbName ?? childField.name })})`;
|
|
598
|
+
}).join(" AND ");
|
|
599
|
+
return `EXISTS (SELECT 1 FROM ${tableRef({ dialect, table: parent.dbName ?? parent.name })} AS ${alias} WHERE ${join})`;
|
|
600
|
+
}
|
|
601
|
+
throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
|
|
602
|
+
}
|
|
603
|
+
function renderRlsPolicy(params) {
|
|
604
|
+
const model = protectedModel(params);
|
|
605
|
+
const expression = predicate({ dialect: params.dialect, schema: params.schema, model });
|
|
606
|
+
const name = identifier({ dialect: params.dialect, name: params.policyName ?? NATIVE_RLS.policyName });
|
|
607
|
+
const table = tableRef({ dialect: params.dialect, table: model.dbName ?? model.name, schema: params.targetSchema });
|
|
608
|
+
return query({ text: `CREATE POLICY ${name} ON ${table} AS PERMISSIVE FOR ALL TO PUBLIC USING (${expression}) WITH CHECK (${expression})` });
|
|
609
|
+
}
|
|
610
|
+
function renderRlsContext(params) {
|
|
611
|
+
requireRls(params);
|
|
612
|
+
if (validateRlsContextIR({ context: params.context }).length !== 0 || Object.keys(params.context).length === 0 || typeof params.values !== "object" || params.values === null || Array.isArray(params.values)) {
|
|
613
|
+
throw new RlsContextError({ reason: "invalid" });
|
|
614
|
+
}
|
|
615
|
+
const collector = new ParamCollector(params.dialect);
|
|
616
|
+
const parts = [];
|
|
617
|
+
for (const [key, slot] of Object.entries(params.context)) {
|
|
618
|
+
if (!Object.hasOwn(params.values, key))
|
|
619
|
+
throw new RlsContextError({ reason: "missing", key });
|
|
620
|
+
const value = params.values[key];
|
|
621
|
+
if (value === null && !slot.nullable || value !== null && (typeof value !== "string" || value.length === 0 || value.includes("\x00"))) {
|
|
622
|
+
throw new RlsContextError({ reason: "invalid", key });
|
|
623
|
+
}
|
|
624
|
+
parts.push(`set_config(${collector.add(`${NATIVE_RLS.settingPrefix}${key}`)}, ${collector.add(value ?? "")}, true)`);
|
|
625
|
+
}
|
|
626
|
+
for (const key of Reflect.ownKeys(params.values)) {
|
|
627
|
+
if (typeof key !== "string" || !Object.hasOwn(params.context, key))
|
|
628
|
+
throw new RlsContextError({ reason: "invalid" });
|
|
629
|
+
}
|
|
630
|
+
return query({ text: `SELECT ${parts.join(", ")}`, values: collector.values });
|
|
631
|
+
}
|
|
632
|
+
function renderRlsRole(params) {
|
|
633
|
+
requireRls(params);
|
|
634
|
+
if (typeof params.role !== "string" || params.role.trim().length === 0 || params.role.includes("\x00")) {
|
|
635
|
+
throw new VibeError3({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
|
|
636
|
+
}
|
|
637
|
+
const collector = new ParamCollector(params.dialect);
|
|
638
|
+
return query({ text: `SELECT set_config('role', ${collector.add(params.role)}, true)`, values: collector.values });
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// packages/sql/src/dialects/postgres-rls-readiness.ts
|
|
642
|
+
var NATIVE_RLS_PROBE_POLICY = "vibeorm_probe";
|
|
643
|
+
var RLS_READINESS_SQL = Object.freeze({
|
|
644
|
+
identity: `SELECT current_user::text AS "currentUser",
|
|
645
|
+
session_user::text AS "sessionUser",
|
|
646
|
+
r.rolsuper AS "super",
|
|
647
|
+
r.rolbypassrls AS "bypassRls",
|
|
648
|
+
r.rolcreatedb AS "createDb",
|
|
649
|
+
r.rolcreaterole AS "createRole",
|
|
650
|
+
pg_catalog.current_setting('server_version_num') AS "serverVersionNum",
|
|
651
|
+
pg_catalog.pg_backend_pid()::text AS "backendPid",
|
|
652
|
+
pg_catalog.pg_postmaster_start_time()::text AS "instanceStart",
|
|
653
|
+
(SELECT a.backend_start::text FROM pg_catalog.pg_stat_activity a WHERE a.pid = pg_catalog.pg_backend_pid()) AS "backendStart",
|
|
654
|
+
pg_catalog.has_database_privilege(current_user, pg_catalog.current_database(), 'TEMP') AS "tempPrivilege"
|
|
655
|
+
FROM pg_catalog.pg_roles r
|
|
656
|
+
WHERE r.rolname = current_user`,
|
|
657
|
+
elevatedRoles: `SELECT r.rolname::text AS "role",
|
|
658
|
+
r.rolsuper AS "super",
|
|
659
|
+
r.rolbypassrls AS "bypassRls",
|
|
660
|
+
r.rolcreaterole AS "createRole",
|
|
661
|
+
r.rolcreatedb AS "createDb"
|
|
662
|
+
FROM pg_catalog.pg_roles r
|
|
663
|
+
WHERE r.rolname <> current_user
|
|
664
|
+
AND pg_catalog.pg_has_role(current_user, r.oid, 'MEMBER')
|
|
665
|
+
AND (r.rolsuper OR r.rolbypassrls OR r.rolcreaterole OR r.rolcreatedb OR r.rolname LIKE 'pg\\_%')
|
|
666
|
+
ORDER BY r.rolname`,
|
|
667
|
+
schemaControl: `SELECT n.nspname::text AS "schema",
|
|
668
|
+
pg_catalog.pg_get_userbyid(n.nspowner)::text AS "owner",
|
|
669
|
+
pg_catalog.pg_has_role(current_user, n.nspowner, 'MEMBER') AS "ownerAccess",
|
|
670
|
+
EXISTS (
|
|
671
|
+
SELECT 1 FROM pg_catalog.pg_roles r
|
|
672
|
+
WHERE pg_catalog.pg_has_role(current_user, r.oid, 'MEMBER')
|
|
673
|
+
AND pg_catalog.has_schema_privilege(r.oid, n.oid, 'CREATE')
|
|
674
|
+
) AS "create",
|
|
675
|
+
pg_catalog.current_database()::text AS "database",
|
|
676
|
+
pg_catalog.pg_get_userbyid(d.datdba)::text AS "databaseOwner",
|
|
677
|
+
pg_catalog.pg_has_role(current_user, d.datdba, 'MEMBER') AS "databaseOwnerAccess"
|
|
678
|
+
FROM pg_catalog.pg_namespace n
|
|
679
|
+
JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database()
|
|
680
|
+
WHERE n.nspname = $1`,
|
|
681
|
+
tables: `SELECT c.relname::text AS "table",
|
|
682
|
+
c.relrowsecurity AS "rlsEnabled",
|
|
683
|
+
c.relforcerowsecurity AS "rlsForced",
|
|
684
|
+
pg_catalog.pg_get_userbyid(c.relowner)::text AS "owner",
|
|
685
|
+
pg_catalog.pg_has_role(current_user, c.relowner, 'MEMBER') AS "ownerAccess",
|
|
686
|
+
EXISTS (
|
|
687
|
+
SELECT 1 FROM pg_catalog.pg_roles r
|
|
688
|
+
WHERE pg_catalog.pg_has_role(current_user, r.oid, 'MEMBER')
|
|
689
|
+
AND pg_catalog.has_table_privilege(r.oid, c.oid, 'TRUNCATE')
|
|
690
|
+
) AS "truncate"
|
|
691
|
+
FROM pg_catalog.pg_class c
|
|
692
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
693
|
+
WHERE n.nspname = $1 AND c.relkind IN ('r', 'p')
|
|
694
|
+
ORDER BY c.relname`,
|
|
695
|
+
policies: `SELECT c.relname::text AS "table",
|
|
696
|
+
p.polname::text AS "name",
|
|
697
|
+
p.polpermissive AS "permissive",
|
|
698
|
+
p.polcmd::text AS "command",
|
|
699
|
+
(p.polroles = '{0}'::oid[]) AS "toPublic",
|
|
700
|
+
(p.polqual IS NOT NULL) AS "hasUsing",
|
|
701
|
+
(p.polwithcheck IS NOT NULL) AS "hasWithCheck"
|
|
702
|
+
FROM pg_catalog.pg_policy p
|
|
703
|
+
JOIN pg_catalog.pg_class c ON c.oid = p.polrelid
|
|
704
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
705
|
+
WHERE n.nspname = $1
|
|
706
|
+
ORDER BY c.relname, p.polname`,
|
|
707
|
+
expressions: `SELECT c.relname::text AS "table",
|
|
708
|
+
p.polname::text AS "name",
|
|
709
|
+
(n.nspname <> $1) AS "probe",
|
|
710
|
+
pg_catalog.pg_get_expr(p.polqual, p.polrelid) AS "using",
|
|
711
|
+
pg_catalog.pg_get_expr(p.polwithcheck, p.polrelid) AS "withCheck"
|
|
712
|
+
FROM pg_catalog.pg_policy p
|
|
713
|
+
JOIN pg_catalog.pg_class c ON c.oid = p.polrelid
|
|
714
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
715
|
+
WHERE (n.nspname = $1 AND p.polname = $2)
|
|
716
|
+
OR (c.relnamespace = pg_catalog.pg_my_temp_schema() AND p.polname = $3)
|
|
717
|
+
ORDER BY c.relname, p.polname`,
|
|
718
|
+
views: `SELECT n.nspname::text AS "schema",
|
|
719
|
+
c.relname::text AS "name",
|
|
720
|
+
pg_catalog.pg_get_userbyid(c.relowner)::text AS "owner",
|
|
721
|
+
(c.relkind = 'm') AS "materialized",
|
|
722
|
+
(COALESCE((
|
|
723
|
+
SELECT o.option_value FROM pg_catalog.pg_options_to_table(c.reloptions) AS o
|
|
724
|
+
WHERE o.option_name = 'security_invoker'
|
|
725
|
+
), 'false') = 'true') AS "securityInvoker"
|
|
726
|
+
FROM pg_catalog.pg_class c
|
|
727
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
728
|
+
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
729
|
+
AND c.relkind IN ('v', 'm')
|
|
730
|
+
AND EXISTS (
|
|
731
|
+
SELECT 1 FROM pg_catalog.pg_roles r
|
|
732
|
+
WHERE pg_catalog.pg_has_role(current_user, r.oid, 'MEMBER')
|
|
733
|
+
AND pg_catalog.has_schema_privilege(r.oid, n.oid, 'USAGE')
|
|
734
|
+
AND (pg_catalog.has_table_privilege(r.oid, c.oid, 'SELECT')
|
|
735
|
+
OR pg_catalog.has_any_column_privilege(r.oid, c.oid, 'SELECT'))
|
|
736
|
+
)
|
|
737
|
+
ORDER BY n.nspname, c.relname`,
|
|
738
|
+
routines: `SELECT n.nspname::text AS "schema",
|
|
739
|
+
p.proname::text AS "name",
|
|
740
|
+
pg_catalog.pg_get_userbyid(p.proowner)::text AS "owner"
|
|
741
|
+
FROM pg_catalog.pg_proc p
|
|
742
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
|
|
743
|
+
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
744
|
+
AND p.prosecdef
|
|
745
|
+
AND EXISTS (
|
|
746
|
+
SELECT 1 FROM pg_catalog.pg_roles r
|
|
747
|
+
WHERE pg_catalog.pg_has_role(current_user, r.oid, 'MEMBER')
|
|
748
|
+
AND pg_catalog.has_schema_privilege(r.oid, n.oid, 'USAGE')
|
|
749
|
+
AND pg_catalog.has_function_privilege(r.oid, p.oid, 'EXECUTE')
|
|
750
|
+
)
|
|
751
|
+
ORDER BY n.nspname, p.proname`,
|
|
752
|
+
triggers: `SELECT c.relname::text AS "table",
|
|
753
|
+
t.tgname::text AS "name",
|
|
754
|
+
f.proname::text AS "function",
|
|
755
|
+
pg_catalog.pg_get_userbyid(f.proowner)::text AS "owner",
|
|
756
|
+
f.prosecdef AS "securityDefiner"
|
|
757
|
+
FROM pg_catalog.pg_trigger t
|
|
758
|
+
JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
|
|
759
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
760
|
+
JOIN pg_catalog.pg_proc f ON f.oid = t.tgfoid
|
|
761
|
+
WHERE n.nspname = $1
|
|
762
|
+
AND NOT t.tgisinternal
|
|
763
|
+
AND f.prosecdef
|
|
764
|
+
ORDER BY c.relname, t.tgname`,
|
|
765
|
+
readSearchPath: `SELECT pg_catalog.current_setting('search_path') AS "searchPath"`,
|
|
766
|
+
setSearchPath: `SELECT pg_catalog.set_config('search_path', $1, true)`
|
|
767
|
+
});
|
|
768
|
+
function requireRls2(params) {
|
|
769
|
+
if (!params.dialect.capabilities.nativeRowLevelSecurity) {
|
|
770
|
+
throw unsupportedCapability4({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
function renderRlsReadinessQuery(params) {
|
|
774
|
+
requireRls2(params);
|
|
775
|
+
const values = params.query === "setSearchPath" ? [params.searchPath ?? ""] : [NATIVE_RLS.tableSchema, NATIVE_RLS.policyName, NATIVE_RLS_PROBE_POLICY];
|
|
776
|
+
const collector = new ParamCollector(params.dialect);
|
|
777
|
+
const slots = new Map;
|
|
778
|
+
const text = RLS_READINESS_SQL[params.query].replace(/\$(\d+)/g, (slot, index) => {
|
|
779
|
+
let placeholder = slots.get(slot);
|
|
780
|
+
if (placeholder === undefined) {
|
|
781
|
+
placeholder = collector.add(values[Number(index) - 1]);
|
|
782
|
+
slots.set(slot, placeholder);
|
|
783
|
+
}
|
|
784
|
+
return placeholder;
|
|
785
|
+
});
|
|
786
|
+
return { text, values: collector.values, returning: "none" };
|
|
787
|
+
}
|
|
788
|
+
function renderRlsProbeTable(params) {
|
|
789
|
+
requireRls2(params);
|
|
790
|
+
const name = params.dialect.quoteIdent({ name: params.table });
|
|
791
|
+
const schema = params.dialect.quoteIdent({ name: params.action === "create" ? NATIVE_RLS.tableSchema : "pg_temp" });
|
|
792
|
+
const text = params.action === "create" ? `CREATE TEMP TABLE ${name} (LIKE ${schema}.${name}) ON COMMIT DROP` : `DROP TABLE ${schema}.${name}`;
|
|
793
|
+
return { text, values: [], returning: "none" };
|
|
794
|
+
}
|
|
795
|
+
function renderRlsPolicyPath(params) {
|
|
796
|
+
requireRls2(params);
|
|
797
|
+
const text = params.stage === "save" ? "SELECT pg_catalog.set_config('vibeorm.rls_saved_search_path', pg_catalog.current_setting('search_path'), true)" : params.stage === "empty" ? "SELECT pg_catalog.set_config('search_path', '', true)" : "SELECT pg_catalog.set_config('search_path', pg_catalog.current_setting('vibeorm.rls_saved_search_path'), true)";
|
|
798
|
+
return { text, values: [], returning: "none" };
|
|
799
|
+
}
|
|
800
|
+
|
|
370
801
|
// packages/sql/src/render.ts
|
|
371
|
-
import { internalError, unsupportedCapability } from "@vibeorm/schema";
|
|
802
|
+
import { internalError, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
|
|
372
803
|
|
|
373
804
|
// packages/sql/src/like.ts
|
|
374
805
|
function escapeLikePattern(params) {
|
|
@@ -395,28 +826,23 @@ function strictMatchPattern(params) {
|
|
|
395
826
|
return op === "contains" ? containsPattern({ raw: value }) : op === "startsWith" ? startsWithPattern({ raw: value }) : endsWithPattern({ raw: value });
|
|
396
827
|
}
|
|
397
828
|
|
|
398
|
-
// packages/sql/src/params.ts
|
|
399
|
-
class ParamCollector {
|
|
400
|
-
dialect;
|
|
401
|
-
values;
|
|
402
|
-
constructor(dialect) {
|
|
403
|
-
this.dialect = dialect;
|
|
404
|
-
this.values = [];
|
|
405
|
-
}
|
|
406
|
-
add(value) {
|
|
407
|
-
this.values.push(value);
|
|
408
|
-
return this.dialect.placeholder({ index: this.values.length });
|
|
409
|
-
}
|
|
410
|
-
addMany(values) {
|
|
411
|
-
return values.map((value) => this.add(value));
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
829
|
// packages/sql/src/render.ts
|
|
416
830
|
function renderStatement(params) {
|
|
417
831
|
const { dialect, statement } = params;
|
|
418
832
|
const collector = new ParamCollector(dialect);
|
|
419
833
|
switch (statement.kind) {
|
|
834
|
+
case "diagnostic": {
|
|
835
|
+
if (isFixedDiagnostic(statement))
|
|
836
|
+
return renderDiagnostic({ dialect, query: statement.query });
|
|
837
|
+
if (statement.query === "postgresExplain") {
|
|
838
|
+
return renderPostgresExplain({
|
|
839
|
+
dialect,
|
|
840
|
+
query: renderStatement({ dialect, statement: statement.statement }),
|
|
841
|
+
...statement.options === undefined ? {} : { options: statement.options }
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
return renderPostgresWorkload({ dialect, ...statement });
|
|
845
|
+
}
|
|
420
846
|
case "select":
|
|
421
847
|
return renderSelect({ dialect, statement, collector });
|
|
422
848
|
case "insert":
|
|
@@ -425,6 +851,28 @@ function renderStatement(params) {
|
|
|
425
851
|
return renderUpdate({ dialect, statement, collector });
|
|
426
852
|
case "delete":
|
|
427
853
|
return renderDelete({ dialect, statement, collector });
|
|
854
|
+
case "rlsReadiness":
|
|
855
|
+
return renderRlsReadinessQuery({ dialect, ...statement });
|
|
856
|
+
case "rlsProbeTable":
|
|
857
|
+
return renderRlsProbeTable({ dialect, ...statement });
|
|
858
|
+
case "rlsPolicyPath":
|
|
859
|
+
return renderRlsPolicyPath({ dialect, ...statement });
|
|
860
|
+
case "rlsEnable":
|
|
861
|
+
return renderRlsEnable({ dialect, table: statement.table });
|
|
862
|
+
case "rlsForce":
|
|
863
|
+
return renderRlsForce({ dialect, table: statement.table });
|
|
864
|
+
case "rlsDisable":
|
|
865
|
+
return renderRlsDisable({ dialect, table: statement.table });
|
|
866
|
+
case "rlsNoForce":
|
|
867
|
+
return renderRlsNoForce({ dialect, table: statement.table });
|
|
868
|
+
case "rlsDropPolicy":
|
|
869
|
+
return renderRlsDropPolicy({ dialect, table: statement.table, policyName: statement.policyName });
|
|
870
|
+
case "rlsPolicy":
|
|
871
|
+
return renderRlsPolicy({ dialect, ...statement });
|
|
872
|
+
case "rlsContext":
|
|
873
|
+
return renderRlsContext({ dialect, context: statement.context, values: statement.values });
|
|
874
|
+
case "rlsRole":
|
|
875
|
+
return renderRlsRole({ dialect, role: statement.role });
|
|
428
876
|
}
|
|
429
877
|
}
|
|
430
878
|
function quoteCol(dialect, column) {
|
|
@@ -494,7 +942,7 @@ function renderAggRef(dialect, agg) {
|
|
|
494
942
|
function assertScalarArrays(dialect, feature) {
|
|
495
943
|
if (dialect.capabilities.scalarArrays)
|
|
496
944
|
return;
|
|
497
|
-
throw
|
|
945
|
+
throw unsupportedCapability5({
|
|
498
946
|
dialect: dialect.name,
|
|
499
947
|
feature,
|
|
500
948
|
hint: "only postgres has native array columns \u2014 model the list as a related table instead"
|
|
@@ -534,6 +982,33 @@ function renderExpr(params) {
|
|
|
534
982
|
const placeholders = collector.addMany(expr.values);
|
|
535
983
|
return `${column} ${expr.negate === true ? "NOT IN" : "IN"} (${placeholders.join(", ")})`;
|
|
536
984
|
}
|
|
985
|
+
case "tupleIn": {
|
|
986
|
+
if (expr.columns.length === 0) {
|
|
987
|
+
throw internalError({ message: "tupleIn needs at least one column" });
|
|
988
|
+
}
|
|
989
|
+
for (const row of expr.rows) {
|
|
990
|
+
if (row.length !== expr.columns.length) {
|
|
991
|
+
throw internalError({
|
|
992
|
+
message: `tupleIn row has ${row.length} values for ${expr.columns.length} columns`
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (expr.rows.length === 0) {
|
|
997
|
+
return expr.negate === true ? "1 = 1" : "1 = 0";
|
|
998
|
+
}
|
|
999
|
+
const columns = expr.columns.map((column) => quoteCol(dialect, column));
|
|
1000
|
+
if (dialect.tupleInStrategy === "rowValue") {
|
|
1001
|
+
const tuples = expr.rows.map((row) => `(${collector.addMany(row).join(", ")})`);
|
|
1002
|
+
const base2 = `(${columns.join(", ")}) IN (${tuples.join(", ")})`;
|
|
1003
|
+
return expr.negate === true ? `NOT (${base2})` : base2;
|
|
1004
|
+
}
|
|
1005
|
+
const conjunctions = expr.rows.map((row) => {
|
|
1006
|
+
const terms = row.map((value, index) => `${columns[index]} = ${collector.add(value)}`);
|
|
1007
|
+
return terms.length === 1 ? terms[0] : `(${terms.join(" AND ")})`;
|
|
1008
|
+
});
|
|
1009
|
+
const base = conjunctions.length === 1 ? conjunctions[0] : `(${conjunctions.join(" OR ")})`;
|
|
1010
|
+
return expr.negate === true ? `NOT ${base.startsWith("(") ? base : `(${base})`}` : base;
|
|
1011
|
+
}
|
|
537
1012
|
case "like": {
|
|
538
1013
|
const column = quoteCol(dialect, expr.column);
|
|
539
1014
|
const placeholder = collector.add(expr.pattern);
|
|
@@ -675,7 +1150,7 @@ function renderJsonLike(params) {
|
|
|
675
1150
|
function renderJsonFilter(params) {
|
|
676
1151
|
const { dialect, expr, collector } = params;
|
|
677
1152
|
if (!dialect.supportedJsonOps.includes(expr.op)) {
|
|
678
|
-
throw
|
|
1153
|
+
throw unsupportedCapability5({
|
|
679
1154
|
dialect: dialect.name,
|
|
680
1155
|
feature: `the json filter "${expr.op}"`,
|
|
681
1156
|
hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
|
|
@@ -730,7 +1205,7 @@ function renderJsonFilter(params) {
|
|
|
730
1205
|
const elements = Array.isArray(expr.value) ? expr.value : [expr.value];
|
|
731
1206
|
const tests = elements.map((element) => {
|
|
732
1207
|
if (element === null || typeof element === "object" && element !== null) {
|
|
733
|
-
throw
|
|
1208
|
+
throw unsupportedCapability5({
|
|
734
1209
|
dialect: dialect.name,
|
|
735
1210
|
feature: "json array_contains with object/array/null elements",
|
|
736
1211
|
hint: "sqlite expresses scalar-element containment only \u2014 deep structural containment needs postgres or mysql"
|
|
@@ -853,7 +1328,7 @@ function renderSelect(params) {
|
|
|
853
1328
|
let distinctPrefix = "";
|
|
854
1329
|
if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
|
|
855
1330
|
if (!dialect.capabilities.distinctOn) {
|
|
856
|
-
throw
|
|
1331
|
+
throw unsupportedCapability5({
|
|
857
1332
|
dialect: dialect.name,
|
|
858
1333
|
feature: "SELECT DISTINCT ON",
|
|
859
1334
|
hint: "postgres-only \u2014 the runtime emulates distinct client-side on this dialect"
|
|
@@ -884,7 +1359,7 @@ function renderSelect(params) {
|
|
|
884
1359
|
}
|
|
885
1360
|
if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
|
|
886
1361
|
if (!dialect.capabilities.lateralJoin) {
|
|
887
|
-
throw
|
|
1362
|
+
throw unsupportedCapability5({
|
|
888
1363
|
dialect: dialect.name,
|
|
889
1364
|
feature: "LATERAL JOIN relation loading",
|
|
890
1365
|
hint: 'use the portable "query" relation strategy on this dialect'
|
|
@@ -958,13 +1433,16 @@ function renderInsert(params) {
|
|
|
958
1433
|
return "DEFAULT";
|
|
959
1434
|
}).join(", ")})`).join(", ");
|
|
960
1435
|
let text = `${insertKeyword} ${renderTable(dialect, statement.table)} (${columnList}) VALUES ${rowsSql}`;
|
|
1436
|
+
if (onConflict !== undefined && !("doNothing" in onConflict) && dialect.excludedRowRef.form === "rowAlias" && Object.values(onConflict.update).some((value) => isExcludedRef(value))) {
|
|
1437
|
+
text += ` AS ${dialect.quoteIdent({ name: dialect.excludedRowRef.alias })}`;
|
|
1438
|
+
}
|
|
961
1439
|
if (onConflict !== undefined) {
|
|
962
1440
|
if ("doNothing" in onConflict) {
|
|
963
1441
|
if (dialect.doNothingForm === "onConflictDoNothing") {
|
|
964
1442
|
let target = "";
|
|
965
1443
|
if (onConflict.constraint !== undefined) {
|
|
966
1444
|
if (dialect.name !== "postgres") {
|
|
967
|
-
throw
|
|
1445
|
+
throw unsupportedCapability5({
|
|
968
1446
|
dialect: dialect.name,
|
|
969
1447
|
feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
|
|
970
1448
|
hint: "only postgres names constraints in conflict targets \u2014 use a column-list target"
|
|
@@ -979,7 +1457,7 @@ function renderInsert(params) {
|
|
|
979
1457
|
}
|
|
980
1458
|
text += ` ON CONFLICT${target} DO NOTHING`;
|
|
981
1459
|
} else if (onConflict.constraint !== undefined || onConflict.conflictWhere !== undefined) {
|
|
982
|
-
throw
|
|
1460
|
+
throw unsupportedCapability5({
|
|
983
1461
|
dialect: dialect.name,
|
|
984
1462
|
feature: "a refined conflict target (constraint / partial-index predicate) on INSERT IGNORE",
|
|
985
1463
|
hint: "mysql skips on any duplicate key \u2014 drop the refinement or gate the call per dialect"
|
|
@@ -991,28 +1469,41 @@ function renderInsert(params) {
|
|
|
991
1469
|
throw internalError({ message: "upsert update must set at least one column" });
|
|
992
1470
|
}
|
|
993
1471
|
const selfRef = dialect.quoteIdent({ name: statement.table.name });
|
|
994
|
-
const assignments = renderAssignments({ dialect, entries: updates, collector, selfRef });
|
|
1472
|
+
const assignments = renderAssignments({ dialect, entries: updates, collector, selfRef, allowExcluded: true });
|
|
995
1473
|
if (dialect.upsertForm === "onConflict") {
|
|
996
1474
|
if (onConflict.lastInsertIdColumn !== undefined) {
|
|
997
1475
|
throw internalError({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
|
|
998
1476
|
}
|
|
999
1477
|
if (onConflict.constraint !== undefined && dialect.name !== "postgres") {
|
|
1000
|
-
throw
|
|
1478
|
+
throw unsupportedCapability5({
|
|
1001
1479
|
dialect: dialect.name,
|
|
1002
1480
|
feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
|
|
1003
1481
|
hint: "sqlite conflict targets are column lists \u2014 name the indexed columns instead"
|
|
1004
1482
|
});
|
|
1005
1483
|
}
|
|
1006
|
-
|
|
1484
|
+
if (onConflict.constraint !== undefined && onConflict.conflictWhere !== undefined) {
|
|
1485
|
+
throw internalError({
|
|
1486
|
+
message: "a named-constraint conflict target already identifies the index \u2014 it cannot carry a partial predicate too"
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
const partial = onConflict.conflictWhere === undefined ? "" : ` WHERE ${onConflict.conflictWhere}`;
|
|
1490
|
+
const target = onConflict.constraint !== undefined ? ` ON CONSTRAINT ${dialect.quoteIdent({ name: onConflict.constraint })}` : ` (${onConflict.columns.map((name) => dialect.quoteIdent({ name })).join(", ")})${partial}`;
|
|
1007
1491
|
text += ` ON CONFLICT${target} DO UPDATE SET ${assignments}`;
|
|
1008
1492
|
} else {
|
|
1009
1493
|
if (onConflict.constraint !== undefined) {
|
|
1010
|
-
throw
|
|
1494
|
+
throw unsupportedCapability5({
|
|
1011
1495
|
dialect: dialect.name,
|
|
1012
1496
|
feature: "a named-constraint conflict target on ON DUPLICATE KEY UPDATE",
|
|
1013
1497
|
hint: "mysql's ODKU fires on any duplicate key \u2014 no target can be named"
|
|
1014
1498
|
});
|
|
1015
1499
|
}
|
|
1500
|
+
if (onConflict.conflictWhere !== undefined) {
|
|
1501
|
+
throw unsupportedCapability5({
|
|
1502
|
+
dialect: dialect.name,
|
|
1503
|
+
feature: "a partial-index conflict target on ON DUPLICATE KEY UPDATE",
|
|
1504
|
+
hint: "mysql's ODKU fires on any duplicate key \u2014 a partial arbiter cannot be selected; gate the call per dialect"
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1016
1507
|
const trick = onConflict.lastInsertIdColumn === undefined ? "" : `, ${dialect.quoteIdent({ name: onConflict.lastInsertIdColumn })} = LAST_INSERT_ID(${dialect.quoteIdent({ name: onConflict.lastInsertIdColumn })})`;
|
|
1017
1508
|
text += ` ON DUPLICATE KEY UPDATE ${assignments}${trick}`;
|
|
1018
1509
|
}
|
|
@@ -1035,11 +1526,25 @@ function renderUpdate(params) {
|
|
|
1035
1526
|
const returning = applyReturning({ dialect, requested: statement.returning });
|
|
1036
1527
|
return { text: text + returning.suffix, values: collector.values, returning: returning.status };
|
|
1037
1528
|
}
|
|
1529
|
+
function renderIncomingRowColumn(params) {
|
|
1530
|
+
const { dialect, column } = params;
|
|
1531
|
+
const ref = dialect.excludedRowRef;
|
|
1532
|
+
const qualifier = ref.form === "keyword" ? ref.name : dialect.quoteIdent({ name: ref.alias });
|
|
1533
|
+
return `${qualifier}.${dialect.quoteIdent({ name: column })}`;
|
|
1534
|
+
}
|
|
1038
1535
|
function renderAssignments(params) {
|
|
1039
1536
|
const { dialect, entries, collector, selfRef } = params;
|
|
1040
1537
|
return entries.map(([name, value]) => {
|
|
1041
1538
|
const column = dialect.quoteIdent({ name });
|
|
1042
1539
|
const read = selfRef === undefined ? column : `${selfRef}.${column}`;
|
|
1540
|
+
if (isExcludedRef(value)) {
|
|
1541
|
+
if (params.allowExcluded !== true) {
|
|
1542
|
+
throw internalError({
|
|
1543
|
+
message: `an incoming-row reference (${value.column}) is only meaningful inside an upsert's update arm`
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
return `${column} = ${renderIncomingRowColumn({ dialect, column: value.column })}`;
|
|
1547
|
+
}
|
|
1043
1548
|
if (isJsonParamCell(value)) {
|
|
1044
1549
|
return `${column} = ${collector.add(value.text)}${dialect.jsonParamCast}`;
|
|
1045
1550
|
}
|
|
@@ -1068,13 +1573,143 @@ function renderDelete(params) {
|
|
|
1068
1573
|
const returning = applyReturning({ dialect, requested: statement.returning });
|
|
1069
1574
|
return { text: text + returning.suffix, values: collector.values, returning: returning.status };
|
|
1070
1575
|
}
|
|
1576
|
+
// packages/sql/src/dialects/postgres-migration.ts
|
|
1577
|
+
import { VibeError as VibeError4, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
|
|
1578
|
+
var SETTING_VALUE = /^[0-9]+(?:\.[0-9]+)?\s*(?:us|ms|s|min|h|d)?$/;
|
|
1579
|
+
function requirePostgres2(params) {
|
|
1580
|
+
if (params.dialect.name !== "postgres") {
|
|
1581
|
+
throw unsupportedCapability6({ dialect: params.dialect.name, feature: params.feature });
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
function renderMigrationSessionRead(params) {
|
|
1585
|
+
requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
|
|
1586
|
+
return {
|
|
1587
|
+
text: `SELECT pg_catalog.current_setting('lock_timeout') AS "lockTimeout", pg_catalog.current_setting('statement_timeout') AS "statementTimeout"`,
|
|
1588
|
+
values: [],
|
|
1589
|
+
returning: "none"
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
function renderMigrationSessionSet(params) {
|
|
1593
|
+
requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
|
|
1594
|
+
const { milliseconds, setting } = params;
|
|
1595
|
+
if (!Number.isSafeInteger(milliseconds) || milliseconds < 0) {
|
|
1596
|
+
throw new VibeError4({
|
|
1597
|
+
code: "VIBE_MIGRATION",
|
|
1598
|
+
message: `${setting} must be a non-negative integer number of milliseconds (got ${String(milliseconds)})`,
|
|
1599
|
+
meta: { reason: "execution-metadata-invalid", setting, value: milliseconds }
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
return { text: `SET ${params.local === true ? "LOCAL " : ""}${setting} = '${milliseconds}ms'`, values: [], returning: "none" };
|
|
1603
|
+
}
|
|
1604
|
+
function renderMigrationSessionRestore(params) {
|
|
1605
|
+
requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
|
|
1606
|
+
const trimmed = params.value.trim();
|
|
1607
|
+
const text = SETTING_VALUE.test(trimmed) ? `SET ${params.setting} = '${trimmed}'` : `RESET ${params.setting}`;
|
|
1608
|
+
return { text, values: [], returning: "none" };
|
|
1609
|
+
}
|
|
1610
|
+
function renderIndexStateQuery(params) {
|
|
1611
|
+
requirePostgres2({ dialect: params.dialect, feature: "concurrentIndexRecovery" });
|
|
1612
|
+
const collector = new ParamCollector(params.dialect);
|
|
1613
|
+
const name = collector.add(params.index);
|
|
1614
|
+
return {
|
|
1615
|
+
text: `SELECT c.relname::text AS "index",
|
|
1616
|
+
t.relname::text AS "table",
|
|
1617
|
+
am.amname::text AS "method",
|
|
1618
|
+
i.indisunique AS "unique",
|
|
1619
|
+
i.indisvalid AS "valid",
|
|
1620
|
+
i.indisready AS "ready",
|
|
1621
|
+
pg_catalog.pg_get_expr(i.indpred, i.indrelid) AS "predicate",
|
|
1622
|
+
pg_catalog.pg_get_indexdef(i.indexrelid) AS "definition",
|
|
1623
|
+
(SELECT string_agg(pg_catalog.pg_get_indexdef(i.indexrelid, k.k, true), E'\\n' ORDER BY k.k)
|
|
1624
|
+
FROM pg_catalog.generate_series(1, i.indnkeyatts) AS k(k)) AS "columns"
|
|
1625
|
+
FROM pg_catalog.pg_class c
|
|
1626
|
+
JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid
|
|
1627
|
+
JOIN pg_catalog.pg_class t ON t.oid = i.indrelid
|
|
1628
|
+
JOIN pg_catalog.pg_am am ON am.oid = c.relam
|
|
1629
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
1630
|
+
WHERE n.nspname = pg_catalog.current_schema() AND c.relname = ${name}`,
|
|
1631
|
+
values: collector.values,
|
|
1632
|
+
returning: "none"
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
function renderConstraintStateQuery(params) {
|
|
1636
|
+
requirePostgres2({ dialect: params.dialect, feature: "stagedConstraints" });
|
|
1637
|
+
const collector = new ParamCollector(params.dialect);
|
|
1638
|
+
const table = collector.add(params.table);
|
|
1639
|
+
const constraint = collector.add(params.constraint);
|
|
1640
|
+
return {
|
|
1641
|
+
text: `SELECT con.conname::text AS "constraint",
|
|
1642
|
+
rel.relname::text AS "table",
|
|
1643
|
+
con.contype::text AS "type",
|
|
1644
|
+
con.convalidated AS "validated",
|
|
1645
|
+
pg_catalog.pg_get_constraintdef(con.oid) AS "definition"
|
|
1646
|
+
FROM pg_catalog.pg_constraint con
|
|
1647
|
+
JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
|
|
1648
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = rel.relnamespace
|
|
1649
|
+
WHERE n.nspname = pg_catalog.current_schema() AND rel.relname = ${table} AND con.conname = ${constraint}`,
|
|
1650
|
+
values: collector.values,
|
|
1651
|
+
returning: "none"
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
function renderRelationStateQuery(params) {
|
|
1655
|
+
requirePostgres2({ dialect: params.dialect, feature: "renameRecovery" });
|
|
1656
|
+
const collector = new ParamCollector(params.dialect);
|
|
1657
|
+
const table = collector.add(params.table);
|
|
1658
|
+
return {
|
|
1659
|
+
text: `SELECT c.relname::text AS "table", c.relkind::text AS "kind"
|
|
1660
|
+
FROM pg_catalog.pg_class c
|
|
1661
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
1662
|
+
WHERE n.nspname = pg_catalog.current_schema() AND c.relname = ${table}`,
|
|
1663
|
+
values: collector.values,
|
|
1664
|
+
returning: "none"
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
function renderColumnStateQuery(params) {
|
|
1668
|
+
requirePostgres2({ dialect: params.dialect, feature: "renameRecovery" });
|
|
1669
|
+
const collector = new ParamCollector(params.dialect);
|
|
1670
|
+
const table = collector.add(params.table);
|
|
1671
|
+
const column = collector.add(params.column);
|
|
1672
|
+
return {
|
|
1673
|
+
text: `SELECT a.attname::text AS "column"
|
|
1674
|
+
FROM pg_catalog.pg_attribute a
|
|
1675
|
+
JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
|
|
1676
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
1677
|
+
WHERE n.nspname = pg_catalog.current_schema() AND c.relname = ${table} AND a.attname = ${column}
|
|
1678
|
+
AND a.attnum > 0 AND NOT a.attisdropped`,
|
|
1679
|
+
values: collector.values,
|
|
1680
|
+
returning: "none"
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1071
1683
|
export {
|
|
1072
1684
|
strictMatchPattern,
|
|
1073
1685
|
startsWithPattern,
|
|
1074
1686
|
sqliteDialect,
|
|
1075
1687
|
renderStatement,
|
|
1688
|
+
renderRlsRole,
|
|
1689
|
+
renderRlsReadinessQuery,
|
|
1690
|
+
renderRlsProbeTable,
|
|
1691
|
+
renderRlsPredicate,
|
|
1692
|
+
renderRlsPolicyPath,
|
|
1693
|
+
renderRlsPolicy,
|
|
1694
|
+
renderRlsNoForce,
|
|
1695
|
+
renderRlsForce,
|
|
1696
|
+
renderRlsEnable,
|
|
1697
|
+
renderRlsDropPolicy,
|
|
1698
|
+
renderRlsDisable,
|
|
1699
|
+
renderRlsContext,
|
|
1700
|
+
renderRlsColumnType,
|
|
1701
|
+
renderRelationStateQuery,
|
|
1702
|
+
renderPostgresWorkload,
|
|
1703
|
+
renderPostgresExplain,
|
|
1704
|
+
renderMigrationSessionSet,
|
|
1705
|
+
renderMigrationSessionRestore,
|
|
1706
|
+
renderMigrationSessionRead,
|
|
1707
|
+
renderIndexStateQuery,
|
|
1076
1708
|
renderFragment,
|
|
1077
1709
|
renderExpr,
|
|
1710
|
+
renderDiagnostic,
|
|
1711
|
+
renderConstraintStateQuery,
|
|
1712
|
+
renderColumnStateQuery,
|
|
1078
1713
|
postgresDialect,
|
|
1079
1714
|
mysqlDialect,
|
|
1080
1715
|
jsonParamCell,
|
|
@@ -1082,10 +1717,14 @@ export {
|
|
|
1082
1717
|
isSetOperation,
|
|
1083
1718
|
isListSetOperation,
|
|
1084
1719
|
isJsonParamCell,
|
|
1720
|
+
isFixedDiagnostic,
|
|
1721
|
+
isExcludedRef,
|
|
1085
1722
|
isDbNowMarker,
|
|
1086
1723
|
getSqlDialect,
|
|
1724
|
+
excludedRef,
|
|
1087
1725
|
escapeLikePattern,
|
|
1088
1726
|
escapeGlobPattern,
|
|
1727
|
+
enumMembershipCodec,
|
|
1089
1728
|
endsWithPattern,
|
|
1090
1729
|
decodeIsNoop,
|
|
1091
1730
|
dbNowMarker,
|
|
@@ -1094,13 +1733,18 @@ export {
|
|
|
1094
1733
|
SQL_DEFAULT_CELL,
|
|
1095
1734
|
SQLITE_CODECS,
|
|
1096
1735
|
SQLITE_CAPABILITIES,
|
|
1736
|
+
RLS_READINESS_SQL,
|
|
1097
1737
|
ParamCollector,
|
|
1738
|
+
POSTGRES_WORKLOAD_LIMITS,
|
|
1739
|
+
POSTGRES_DIAGNOSTIC_SQL,
|
|
1098
1740
|
POSTGRES_CODECS,
|
|
1099
1741
|
POSTGRES_CAPABILITIES,
|
|
1742
|
+
NATIVE_RLS_PROBE_POLICY,
|
|
1743
|
+
NATIVE_RLS,
|
|
1100
1744
|
MYSQL_CODECS,
|
|
1101
1745
|
MYSQL_CAPABILITIES,
|
|
1102
1746
|
DIALECT_CODECS,
|
|
1103
1747
|
DIALECT_CAPABILITIES
|
|
1104
1748
|
};
|
|
1105
1749
|
|
|
1106
|
-
//# debugId=
|
|
1750
|
+
//# debugId=BFF5BB4016D0E8C364756E2164756E21
|