rake-db 2.36.6 → 2.36.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/index.d.ts +130 -12
- package/dist/index.js +232 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,15 @@ declare namespace DbStructure {
|
|
|
37
37
|
columns: Column[];
|
|
38
38
|
sql: string;
|
|
39
39
|
}
|
|
40
|
+
interface MaterializedView {
|
|
41
|
+
schemaName: string;
|
|
42
|
+
name: string;
|
|
43
|
+
deps: RakeDbAst.MaterializedView['deps'];
|
|
44
|
+
columns: Column[];
|
|
45
|
+
sql: string;
|
|
46
|
+
isPopulated: boolean;
|
|
47
|
+
tablespace?: string;
|
|
48
|
+
}
|
|
40
49
|
interface Procedure {
|
|
41
50
|
schemaName: string;
|
|
42
51
|
name: string;
|
|
@@ -198,7 +207,8 @@ interface IntrospectedStructure {
|
|
|
198
207
|
version: number;
|
|
199
208
|
schemas: string[];
|
|
200
209
|
tables: DbStructure.Table[];
|
|
201
|
-
views
|
|
210
|
+
views?: DbStructure.View[];
|
|
211
|
+
materializedViews?: DbStructure.MaterializedView[];
|
|
202
212
|
indexes: DbStructure.Index[];
|
|
203
213
|
excludes: DbStructure.Exclude[];
|
|
204
214
|
constraints: DbStructure.Constraint[];
|
|
@@ -217,12 +227,13 @@ interface IntrospectDbStructureParams {
|
|
|
217
227
|
roles?: {
|
|
218
228
|
whereSql?: string;
|
|
219
229
|
};
|
|
230
|
+
loadViews?: boolean;
|
|
220
231
|
loadDefaultPrivileges?: boolean;
|
|
221
232
|
loadGrants?: boolean;
|
|
222
233
|
}
|
|
223
234
|
declare function getDbVersion(db: Adapter): Promise<number>;
|
|
224
235
|
declare function introspectDbSchema(db: Adapter, params?: IntrospectDbStructureParams): Promise<IntrospectedStructure>;
|
|
225
|
-
type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameType | RakeDbAst.Schema | RakeDbAst.RenameSchema | RakeDbAst.Extension | RakeDbAst.Enum | RakeDbAst.EnumValues | RakeDbAst.RenameEnumValues | RakeDbAst.ChangeEnumValues | RakeDbAst.Domain | RakeDbAst.Collation | RakeDbAst.Constraint | RakeDbAst.RenameTableItem | RakeDbAst.View | RakeDbAst.Role | RakeDbAst.RenameRole | RakeDbAst.ChangeRole | RakeDbAst.DefaultPrivilege | RakeDbAst.TableRls | RakeDbAst.Policy | RakeDbAst.PolicyChange | RakeDbAst.Grant;
|
|
236
|
+
type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameType | RakeDbAst.Schema | RakeDbAst.RenameSchema | RakeDbAst.Extension | RakeDbAst.Enum | RakeDbAst.EnumValues | RakeDbAst.RenameEnumValues | RakeDbAst.ChangeEnumValues | RakeDbAst.Domain | RakeDbAst.Collation | RakeDbAst.Constraint | RakeDbAst.RenameTableItem | RakeDbAst.View | RakeDbAst.MaterializedView | RakeDbAst.Role | RakeDbAst.RenameRole | RakeDbAst.ChangeRole | RakeDbAst.DefaultPrivilege | RakeDbAst.TableRls | RakeDbAst.Policy | RakeDbAst.PolicyChange | RakeDbAst.Grant;
|
|
226
237
|
declare namespace RakeDbAst {
|
|
227
238
|
export interface Table extends TableData {
|
|
228
239
|
type: 'table';
|
|
@@ -408,11 +419,83 @@ declare namespace RakeDbAst {
|
|
|
408
419
|
temporary?: boolean;
|
|
409
420
|
recursive?: boolean;
|
|
410
421
|
columns?: string[];
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
422
|
+
checkOption?: 'LOCAL' | 'CASCADED';
|
|
423
|
+
securityBarrier?: boolean;
|
|
424
|
+
securityInvoker?: boolean;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Materialized view migration AST item.
|
|
428
|
+
*/
|
|
429
|
+
export interface MaterializedView {
|
|
430
|
+
/**
|
|
431
|
+
* Identifies the AST item as a materialized view.
|
|
432
|
+
*/
|
|
433
|
+
type: 'materializedView';
|
|
434
|
+
/**
|
|
435
|
+
* Creates or drops the materialized view.
|
|
436
|
+
*/
|
|
437
|
+
action: 'create' | 'drop';
|
|
438
|
+
/**
|
|
439
|
+
* Schema containing the materialized view.
|
|
440
|
+
*/
|
|
441
|
+
schema?: string;
|
|
442
|
+
/**
|
|
443
|
+
* Materialized view name without schema.
|
|
444
|
+
*/
|
|
445
|
+
name: string;
|
|
446
|
+
/**
|
|
447
|
+
* Column shape used by generated migration dependency analysis.
|
|
448
|
+
*/
|
|
449
|
+
shape: ColumnsShape;
|
|
450
|
+
/**
|
|
451
|
+
* SQL query used to define the materialized view.
|
|
452
|
+
*/
|
|
453
|
+
sql: RawSqlBase;
|
|
454
|
+
/**
|
|
455
|
+
* Options used when creating or dropping the materialized view.
|
|
456
|
+
*/
|
|
457
|
+
options: MaterializedViewOptions;
|
|
458
|
+
/**
|
|
459
|
+
* Relations referenced by the materialized view SQL definition.
|
|
460
|
+
*/
|
|
461
|
+
deps: {
|
|
462
|
+
schemaName: string;
|
|
463
|
+
name: string;
|
|
464
|
+
}[];
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Options for materialized view create and drop migrations.
|
|
468
|
+
*/
|
|
469
|
+
export interface MaterializedViewOptions {
|
|
470
|
+
/**
|
|
471
|
+
* Drops the materialized view only when it exists.
|
|
472
|
+
*/
|
|
473
|
+
dropIfExists?: boolean;
|
|
474
|
+
/**
|
|
475
|
+
* Drop behavior for dependent objects.
|
|
476
|
+
*/
|
|
477
|
+
dropMode?: DropMode;
|
|
478
|
+
/**
|
|
479
|
+
* Explicit materialized view column names.
|
|
480
|
+
*/
|
|
481
|
+
columns?: string[];
|
|
482
|
+
/**
|
|
483
|
+
* Adds WITH DATA or WITH NO DATA when creating the materialized view.
|
|
484
|
+
*/
|
|
485
|
+
withData?: boolean;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Options for refreshing a materialized view.
|
|
489
|
+
*/
|
|
490
|
+
export interface RefreshMaterializedViewOptions {
|
|
491
|
+
/**
|
|
492
|
+
* Refreshes without blocking concurrent selects.
|
|
493
|
+
*/
|
|
494
|
+
concurrently?: boolean;
|
|
495
|
+
/**
|
|
496
|
+
* Adds WITH DATA or WITH NO DATA to the refresh statement.
|
|
497
|
+
*/
|
|
498
|
+
withData?: boolean;
|
|
416
499
|
}
|
|
417
500
|
export interface Role extends DbStructure.Role {
|
|
418
501
|
type: 'role';
|
|
@@ -1487,11 +1570,9 @@ declare class Migration<CT = unknown> {
|
|
|
1487
1570
|
* temporary: true,
|
|
1488
1571
|
* recursive: true,
|
|
1489
1572
|
* columns: ['n'],
|
|
1490
|
-
*
|
|
1491
|
-
*
|
|
1492
|
-
*
|
|
1493
|
-
* securityInvoker: true,
|
|
1494
|
-
* },
|
|
1573
|
+
* checkOption: 'LOCAL', // or 'CASCADED'
|
|
1574
|
+
* securityBarrier: true,
|
|
1575
|
+
* securityInvoker: true,
|
|
1495
1576
|
* },
|
|
1496
1577
|
* `
|
|
1497
1578
|
* VALUES (1)
|
|
@@ -1529,6 +1610,43 @@ declare class Migration<CT = unknown> {
|
|
|
1529
1610
|
* @param sql - SQL to create the view with
|
|
1530
1611
|
*/
|
|
1531
1612
|
dropView(name: string, sql: string | RawSqlBase): Promise<void>;
|
|
1613
|
+
/**
|
|
1614
|
+
* Create a materialized view, drop it on rollback.
|
|
1615
|
+
*
|
|
1616
|
+
* @param name - name of the materialized view
|
|
1617
|
+
* @param options - materialized view options
|
|
1618
|
+
* @param sql - SQL to create the materialized view with
|
|
1619
|
+
*/
|
|
1620
|
+
createMaterializedView(name: string, options: RakeDbAst.MaterializedViewOptions, sql: string | RawSqlBase): Promise<void>;
|
|
1621
|
+
/**
|
|
1622
|
+
* Create a materialized view, drop it on rollback.
|
|
1623
|
+
*
|
|
1624
|
+
* @param name - name of the materialized view
|
|
1625
|
+
* @param sql - SQL to create the materialized view with
|
|
1626
|
+
*/
|
|
1627
|
+
createMaterializedView(name: string, sql: string | RawSqlBase): Promise<void>;
|
|
1628
|
+
/**
|
|
1629
|
+
* Drop a materialized view, create it on rollback.
|
|
1630
|
+
*
|
|
1631
|
+
* @param name - name of the materialized view
|
|
1632
|
+
* @param options - materialized view options
|
|
1633
|
+
* @param sql - SQL to create the materialized view with
|
|
1634
|
+
*/
|
|
1635
|
+
dropMaterializedView(name: string, options: RakeDbAst.MaterializedViewOptions, sql: string | RawSqlBase): Promise<void>;
|
|
1636
|
+
/**
|
|
1637
|
+
* Drop a materialized view, create it on rollback.
|
|
1638
|
+
*
|
|
1639
|
+
* @param name - name of the materialized view
|
|
1640
|
+
* @param sql - SQL to create the materialized view with
|
|
1641
|
+
*/
|
|
1642
|
+
dropMaterializedView(name: string, sql: string | RawSqlBase): Promise<void>;
|
|
1643
|
+
/**
|
|
1644
|
+
* Refresh a materialized view.
|
|
1645
|
+
*
|
|
1646
|
+
* @param name - name of the materialized view
|
|
1647
|
+
* @param options - refresh options
|
|
1648
|
+
*/
|
|
1649
|
+
refreshMaterializedView(name: string, options?: RakeDbAst.RefreshMaterializedViewOptions): Promise<void>;
|
|
1532
1650
|
/**
|
|
1533
1651
|
* Returns boolean to know if table exists:
|
|
1534
1652
|
*
|
package/dist/index.js
CHANGED
|
@@ -427,7 +427,7 @@ const createTable$1 = async (migration, tableMethods, up, tableName, first, seco
|
|
|
427
427
|
});
|
|
428
428
|
} else shape = tableData = pqb_internal.emptyObject;
|
|
429
429
|
const schema = migration.adapter.getSchema();
|
|
430
|
-
const ast = makeAst$
|
|
430
|
+
const ast = makeAst$5(schema, up, tableName, shape, tableData, options, migration.options.noPrimaryKey);
|
|
431
431
|
fn && validatePrimaryKey(ast);
|
|
432
432
|
const queries = astToQueries$1(schema, ast, snakeCase, language);
|
|
433
433
|
for (const { then, ...query } of queries) {
|
|
@@ -442,7 +442,7 @@ const createTable$1 = async (migration, tableMethods, up, tableName, first, seco
|
|
|
442
442
|
});
|
|
443
443
|
} };
|
|
444
444
|
};
|
|
445
|
-
const makeAst$
|
|
445
|
+
const makeAst$5 = (schema, up, tableName, shape, tableData, options, noPrimaryKey) => {
|
|
446
446
|
const shapePKeys = [];
|
|
447
447
|
for (const key in shape) if (shape[key].data.primaryKey) shapePKeys.push(key);
|
|
448
448
|
const { primaryKey } = tableData;
|
|
@@ -860,13 +860,13 @@ const changeTable = async (migration, tableChangeMethods, up, tableName, options
|
|
|
860
860
|
standaloneCheckChanges.length = 0;
|
|
861
861
|
const changeData = fn?.(tableChanger) || {};
|
|
862
862
|
const schema = migration.adapter.getSchema();
|
|
863
|
-
const queries = astToQueries(schema, makeAst$
|
|
863
|
+
const queries = astToQueries(schema, makeAst$4(schema, up, tableName, changeData, changeTableData, options), snakeCase, language);
|
|
864
864
|
for (const query of queries) {
|
|
865
865
|
const result = await migration.adapter.arrays(interpolateSqlValues(query));
|
|
866
866
|
query.then?.(result);
|
|
867
867
|
}
|
|
868
868
|
};
|
|
869
|
-
const makeAst$
|
|
869
|
+
const makeAst$4 = (schema, up, name, changeData, changeTableData, options) => {
|
|
870
870
|
const { comment } = options;
|
|
871
871
|
const shape = {};
|
|
872
872
|
const consumedChanges = {};
|
|
@@ -1139,10 +1139,10 @@ const renameColumnSql = (from, to) => {
|
|
|
1139
1139
|
return `RENAME COLUMN "${from}" TO "${to}"`;
|
|
1140
1140
|
};
|
|
1141
1141
|
const createView = async (migration, up, name, options, sql) => {
|
|
1142
|
-
const query = astToQuery$
|
|
1142
|
+
const query = astToQuery$2(makeAst$3(migration.adapter.getSchema(), up, name, options, sql));
|
|
1143
1143
|
await migration.adapter.arrays(interpolateSqlValues(query));
|
|
1144
1144
|
};
|
|
1145
|
-
const makeAst$
|
|
1145
|
+
const makeAst$3 = (schema, up, fullName, options, sql) => {
|
|
1146
1146
|
if (typeof sql === "string") sql = (0, pqb_internal.raw)({ raw: sql });
|
|
1147
1147
|
const [s, name] = getSchemaAndTableFromName(schema, fullName);
|
|
1148
1148
|
return {
|
|
@@ -1156,7 +1156,7 @@ const makeAst$2 = (schema, up, fullName, options, sql) => {
|
|
|
1156
1156
|
deps: []
|
|
1157
1157
|
};
|
|
1158
1158
|
};
|
|
1159
|
-
const astToQuery$
|
|
1159
|
+
const astToQuery$2 = (ast) => {
|
|
1160
1160
|
const values = [];
|
|
1161
1161
|
const sql = [];
|
|
1162
1162
|
const { options } = ast;
|
|
@@ -1168,11 +1168,10 @@ const astToQuery$1 = (ast) => {
|
|
|
1168
1168
|
if (options?.recursive) sql.push("RECURSIVE");
|
|
1169
1169
|
sql.push(`VIEW ${sqlName}`);
|
|
1170
1170
|
if (options?.columns) sql.push(`(${options.columns.map((column) => `"${column}"`).join(", ")})`);
|
|
1171
|
-
const withOptions = options?.with;
|
|
1172
1171
|
const list = [];
|
|
1173
|
-
if (
|
|
1174
|
-
if (
|
|
1175
|
-
if (
|
|
1172
|
+
if (options?.checkOption) list.push(`check_option = ${(0, pqb_internal.singleQuote)(options.checkOption)}`);
|
|
1173
|
+
if (options?.securityBarrier) list.push(`security_barrier = true`);
|
|
1174
|
+
if (options?.securityInvoker !== false) list.push(`security_invoker = true`);
|
|
1176
1175
|
if (list.length) sql.push(`WITH ( ${list.join(", ")} )`);
|
|
1177
1176
|
sql.push(`AS (${ast.sql.toSQL({ values })})`);
|
|
1178
1177
|
} else {
|
|
@@ -1186,6 +1185,58 @@ const astToQuery$1 = (ast) => {
|
|
|
1186
1185
|
values
|
|
1187
1186
|
};
|
|
1188
1187
|
};
|
|
1188
|
+
const createMaterializedView = async (migration, up, name, options, sql) => {
|
|
1189
|
+
const query = astToQuery$1(makeAst$2(migration.adapter.getSchema(), up, name, options, sql));
|
|
1190
|
+
await migration.adapter.arrays(interpolateSqlValues(query));
|
|
1191
|
+
};
|
|
1192
|
+
const refreshMaterializedView = async (migration, name, options = {}) => {
|
|
1193
|
+
if (options.concurrently && options.withData === false) throw new Error("Cannot refresh a materialized view concurrently with WITH NO DATA");
|
|
1194
|
+
const [s, viewName] = getSchemaAndTableFromName(migration.adapter.getSchema(), name);
|
|
1195
|
+
const sql = ["REFRESH MATERIALIZED VIEW"];
|
|
1196
|
+
if (options.concurrently) sql.push("CONCURRENTLY");
|
|
1197
|
+
sql.push(`${s ? `"${s}".` : ""}"${viewName}"`);
|
|
1198
|
+
pushWithData(sql, options);
|
|
1199
|
+
await migration.adapter.arrays(sql.join(" "));
|
|
1200
|
+
};
|
|
1201
|
+
const makeAst$2 = (schema, up, fullName, options, sql) => {
|
|
1202
|
+
if (typeof sql === "string") sql = (0, pqb_internal.raw)({ raw: sql });
|
|
1203
|
+
const [s, name] = getSchemaAndTableFromName(schema, fullName);
|
|
1204
|
+
return {
|
|
1205
|
+
type: "materializedView",
|
|
1206
|
+
action: up ? "create" : "drop",
|
|
1207
|
+
schema: s,
|
|
1208
|
+
name,
|
|
1209
|
+
shape: {},
|
|
1210
|
+
sql,
|
|
1211
|
+
options,
|
|
1212
|
+
deps: []
|
|
1213
|
+
};
|
|
1214
|
+
};
|
|
1215
|
+
const astToQuery$1 = (ast) => {
|
|
1216
|
+
const values = [];
|
|
1217
|
+
const sql = [];
|
|
1218
|
+
const { options } = ast;
|
|
1219
|
+
const sqlName = `${ast.schema ? `"${ast.schema}".` : ""}"${ast.name}"`;
|
|
1220
|
+
if (ast.action === "create") {
|
|
1221
|
+
sql.push(`CREATE MATERIALIZED VIEW ${sqlName}`);
|
|
1222
|
+
if (options?.columns) sql.push(`(${options.columns.map((column) => `"${column}"`).join(", ")})`);
|
|
1223
|
+
sql.push(`AS (${ast.sql.toSQL({ values })})`);
|
|
1224
|
+
pushWithData(sql, options);
|
|
1225
|
+
} else {
|
|
1226
|
+
sql.push("DROP MATERIALIZED VIEW");
|
|
1227
|
+
if (options?.dropIfExists) sql.push(`IF EXISTS`);
|
|
1228
|
+
sql.push(sqlName);
|
|
1229
|
+
if (options?.dropMode) sql.push(options.dropMode);
|
|
1230
|
+
}
|
|
1231
|
+
return {
|
|
1232
|
+
text: sql.join(" "),
|
|
1233
|
+
values
|
|
1234
|
+
};
|
|
1235
|
+
};
|
|
1236
|
+
const pushWithData = (sql, options) => {
|
|
1237
|
+
if (options.withData === true) sql.push("WITH DATA");
|
|
1238
|
+
else if (options.withData === false) sql.push("WITH NO DATA");
|
|
1239
|
+
};
|
|
1189
1240
|
const serializers = {
|
|
1190
1241
|
super: (b) => `${b ? "" : "NO"}SUPERUSER`,
|
|
1191
1242
|
inherit: (b) => `${b ? "" : "NO"}INHERIT`,
|
|
@@ -2383,6 +2434,23 @@ var Migration = class {
|
|
|
2383
2434
|
const [options, sql] = args.length === 2 ? args : [pqb_internal.emptyObject, args[0]];
|
|
2384
2435
|
return createView(this, !this.up, name, options, sql);
|
|
2385
2436
|
}
|
|
2437
|
+
createMaterializedView(name, ...args) {
|
|
2438
|
+
const [options, sql] = args.length === 2 ? args : [pqb_internal.emptyObject, args[0]];
|
|
2439
|
+
return createMaterializedView(this, this.up, name, options, sql);
|
|
2440
|
+
}
|
|
2441
|
+
dropMaterializedView(name, ...args) {
|
|
2442
|
+
const [options, sql] = args.length === 2 ? args : [pqb_internal.emptyObject, args[0]];
|
|
2443
|
+
return createMaterializedView(this, !this.up, name, options, sql);
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* Refresh a materialized view.
|
|
2447
|
+
*
|
|
2448
|
+
* @param name - name of the materialized view
|
|
2449
|
+
* @param options - refresh options
|
|
2450
|
+
*/
|
|
2451
|
+
refreshMaterializedView(name, options) {
|
|
2452
|
+
return refreshMaterializedView(this, name, options);
|
|
2453
|
+
}
|
|
2386
2454
|
/**
|
|
2387
2455
|
* Returns boolean to know if table exists:
|
|
2388
2456
|
*
|
|
@@ -3709,10 +3777,11 @@ const astToGenerateItem = (config, ast, currentSchema) => {
|
|
|
3709
3777
|
switch (ast.type) {
|
|
3710
3778
|
case "table":
|
|
3711
3779
|
case "changeTable":
|
|
3712
|
-
case "view":
|
|
3780
|
+
case "view":
|
|
3781
|
+
case "materializedView": {
|
|
3713
3782
|
const schema = ast.schema ?? currentSchema;
|
|
3714
3783
|
const table = `${schema}.${ast.name}`;
|
|
3715
|
-
if (ast.type === "table" || ast.type === "view") {
|
|
3784
|
+
if (ast.type === "table" || ast.type === "view" || ast.type === "materializedView") {
|
|
3716
3785
|
const keys = ast.action === "create" ? add : drop;
|
|
3717
3786
|
keys.push(table);
|
|
3718
3787
|
deps.push(schema);
|
|
@@ -4251,31 +4320,41 @@ const astEncoders = {
|
|
|
4251
4320
|
})}, ${(0, pqb_internal.singleQuote)(ast.from)}, ${(0, pqb_internal.singleQuote)(ast.to)});`];
|
|
4252
4321
|
},
|
|
4253
4322
|
view(ast, _, currentSchema) {
|
|
4254
|
-
const code = [`await db.
|
|
4323
|
+
const code = [`await db.${ast.action}View(${quoteSchemaTable(ast, currentSchema)}`];
|
|
4255
4324
|
const options = [];
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
if (
|
|
4259
|
-
if (
|
|
4260
|
-
if (
|
|
4325
|
+
const { options: astOptions } = ast;
|
|
4326
|
+
if (astOptions.createOrReplace !== void 0) options.push(`createOrReplace: ${astOptions.createOrReplace},`);
|
|
4327
|
+
if (astOptions.dropIfExists !== void 0) options.push(`dropIfExists: ${astOptions.dropIfExists},`);
|
|
4328
|
+
if (astOptions.dropMode) options.push(`dropMode: '${astOptions.dropMode}',`);
|
|
4329
|
+
if (astOptions.temporary !== void 0) options.push(`temporary: ${astOptions.temporary},`);
|
|
4330
|
+
if (astOptions.recursive !== void 0) options.push(`recursive: ${astOptions.recursive},`);
|
|
4331
|
+
if (astOptions.columns) options.push(`columns: [${astOptions.columns.map(pqb_internal.singleQuote).join(", ")}],`);
|
|
4332
|
+
if (astOptions.checkOption) options.push(`checkOption: '${astOptions.checkOption}',`);
|
|
4333
|
+
if (astOptions.securityBarrier !== void 0) options.push(`securityBarrier: ${astOptions.securityBarrier},`);
|
|
4334
|
+
if (astOptions.securityInvoker !== void 0) options.push(`securityInvoker: ${astOptions.securityInvoker},`);
|
|
4261
4335
|
if (options.length) {
|
|
4262
4336
|
(0, pqb_internal.addCode)(code, ", {");
|
|
4263
4337
|
code.push(options, "}");
|
|
4264
4338
|
}
|
|
4265
4339
|
(0, pqb_internal.addCode)(code, ", ");
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4340
|
+
addViewSqlCode(code, ast.sql);
|
|
4341
|
+
(0, pqb_internal.addCode)(code, ");");
|
|
4342
|
+
return code;
|
|
4343
|
+
},
|
|
4344
|
+
materializedView(ast, _, currentSchema) {
|
|
4345
|
+
const code = [`await db.${ast.action}MaterializedView(${quoteSchemaTable(ast, currentSchema)}`];
|
|
4346
|
+
const options = [];
|
|
4347
|
+
const { options: astOptions } = ast;
|
|
4348
|
+
if (astOptions.dropIfExists !== void 0) options.push(`dropIfExists: ${astOptions.dropIfExists},`);
|
|
4349
|
+
if (astOptions.dropMode) options.push(`dropMode: '${astOptions.dropMode}',`);
|
|
4350
|
+
if (astOptions.columns) options.push(`columns: [${astOptions.columns.map(pqb_internal.singleQuote).join(", ")}],`);
|
|
4351
|
+
if (astOptions.withData !== void 0) options.push(`withData: ${astOptions.withData},`);
|
|
4352
|
+
if (options.length) {
|
|
4353
|
+
(0, pqb_internal.addCode)(code, ", {");
|
|
4354
|
+
code.push(options, "}");
|
|
4355
|
+
}
|
|
4356
|
+
(0, pqb_internal.addCode)(code, ", ");
|
|
4357
|
+
addViewSqlCode(code, ast.sql);
|
|
4279
4358
|
(0, pqb_internal.addCode)(code, ");");
|
|
4280
4359
|
return code;
|
|
4281
4360
|
},
|
|
@@ -4451,6 +4530,21 @@ const isPolicyRecreateDefinition = (policy) => {
|
|
|
4451
4530
|
const rawSqlStringToCode = (sql) => {
|
|
4452
4531
|
return `db.sql${(0, pqb_internal.backtickQuote)(sql)}`;
|
|
4453
4532
|
};
|
|
4533
|
+
const addViewSqlCode = (code, sqlAst) => {
|
|
4534
|
+
if (!sqlAst._values) {
|
|
4535
|
+
const raw = sqlAst._sql;
|
|
4536
|
+
let sql;
|
|
4537
|
+
if (typeof raw === "string") sql = raw;
|
|
4538
|
+
else {
|
|
4539
|
+
sql = "";
|
|
4540
|
+
const parts = raw[0];
|
|
4541
|
+
const last = parts.length - 1;
|
|
4542
|
+
for (let i = 0; i < last; i++) sql += parts[i] + `\${${raw[i + 1]}}`;
|
|
4543
|
+
sql += parts[last];
|
|
4544
|
+
}
|
|
4545
|
+
(0, pqb_internal.addCode)(code, (0, pqb_internal.backtickQuote)(sql));
|
|
4546
|
+
} else (0, pqb_internal.addCode)(code, (0, pqb_internal.rawSqlToCode)(sqlAst, "db"));
|
|
4547
|
+
};
|
|
4454
4548
|
const roleParams = (name, ast, compare, to) => {
|
|
4455
4549
|
const params = [];
|
|
4456
4550
|
for (const key of [
|
|
@@ -4635,6 +4729,39 @@ JOIN pg_class c
|
|
|
4635
4729
|
JOIN pg_rewrite r ON r.ev_class = c.oid
|
|
4636
4730
|
WHERE ${filterSchema("nc.nspname")}
|
|
4637
4731
|
ORDER BY c.relname`;
|
|
4732
|
+
const materializedViewsSql = `SELECT
|
|
4733
|
+
nc.nspname AS "schemaName",
|
|
4734
|
+
c.relname AS "name",
|
|
4735
|
+
(
|
|
4736
|
+
SELECT COALESCE(json_agg(t.*), '[]')
|
|
4737
|
+
FROM (
|
|
4738
|
+
SELECT
|
|
4739
|
+
ns.nspname AS "schemaName",
|
|
4740
|
+
obj.relname AS "name"
|
|
4741
|
+
FROM pg_class obj
|
|
4742
|
+
JOIN pg_depend dep ON dep.refobjid = obj.oid
|
|
4743
|
+
JOIN pg_rewrite rew ON rew.oid = dep.objid
|
|
4744
|
+
JOIN pg_namespace ns ON ns.oid = obj.relnamespace
|
|
4745
|
+
WHERE rew.ev_class = c.oid AND obj.oid <> c.oid
|
|
4746
|
+
) t
|
|
4747
|
+
) "deps",
|
|
4748
|
+
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
4749
|
+
schema: "nc",
|
|
4750
|
+
table: "c",
|
|
4751
|
+
where: "a.attrelid = c.oid"
|
|
4752
|
+
})}) t) AS "columns",
|
|
4753
|
+
pg_get_viewdef(c.oid) AS "sql",
|
|
4754
|
+
c.relispopulated AS "isPopulated",
|
|
4755
|
+
spc.spcname AS "tablespace"
|
|
4756
|
+
FROM pg_namespace nc
|
|
4757
|
+
JOIN pg_class c
|
|
4758
|
+
ON nc.oid = c.relnamespace
|
|
4759
|
+
AND c.relkind = 'm'
|
|
4760
|
+
AND c.relpersistence != 't'
|
|
4761
|
+
JOIN pg_rewrite r ON r.ev_class = c.oid
|
|
4762
|
+
LEFT JOIN pg_tablespace spc ON spc.oid = c.reltablespace
|
|
4763
|
+
WHERE ${filterSchema("nc.nspname")}
|
|
4764
|
+
ORDER BY c.relname`;
|
|
4638
4765
|
const indexesSql = `SELECT
|
|
4639
4766
|
n.nspname "schemaName",
|
|
4640
4767
|
t.relname "tableName",
|
|
@@ -4936,7 +5063,7 @@ const grantsSql = `SELECT COALESCE(json_agg(t.* ORDER BY t."target", t."schema",
|
|
|
4936
5063
|
FROM pg_class c
|
|
4937
5064
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
4938
5065
|
JOIN LATERAL aclexplode(coalesce(c.relacl, acldefault('r', c.relowner))) ae ON true
|
|
4939
|
-
WHERE c.relkind IN ('r', 'p')
|
|
5066
|
+
WHERE c.relkind IN ('r', 'p', 'v')
|
|
4940
5067
|
AND ${filterSchema("n.nspname")}
|
|
4941
5068
|
AND ae.grantee <> c.relowner
|
|
4942
5069
|
GROUP BY "grantor", "grantee", "schema", "name", "target"
|
|
@@ -5068,7 +5195,7 @@ FROM pg_policy p
|
|
|
5068
5195
|
JOIN pg_class c ON c.oid = p.polrelid
|
|
5069
5196
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
5070
5197
|
ORDER BY n.nspname, c.relname, p.polname`;
|
|
5071
|
-
const sql = (version, params) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(tablesSql(params?.rls), "tables")}, ${jsonAgg(viewsSql, "views")}, ${jsonAgg(indexesSql, "indexes")}, ${jsonAgg(constraintsSql, "constraints")}, ${jsonAgg(triggersSql, "triggers")}, ${jsonAgg(extensionsSql, "extensions")}, ${jsonAgg(enumsSql, "enums")}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(collationsSql(version), "collations")}${params?.roles ? `, (${roleSql(params.roles)}) AS "roles"` : ""}${params?.loadDefaultPrivileges ? `, (${defaultPrivilegesSql}) AS "defaultPrivileges"` : ""}${params?.loadGrants ? `, (${grantsSql}) AS "grants"` : ""}${params?.rls ? `, ${jsonAgg(policiesSql, "policies")}` : ""}`;
|
|
5198
|
+
const sql = (version, params) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(tablesSql(params?.rls), "tables")}, ${params?.loadViews ? `${jsonAgg(viewsSql, "views")}, ${jsonAgg(materializedViewsSql, "materializedViews")}, ` : ""}${jsonAgg(indexesSql, "indexes")}, ${jsonAgg(constraintsSql, "constraints")}, ${jsonAgg(triggersSql, "triggers")}, ${jsonAgg(extensionsSql, "extensions")}, ${jsonAgg(enumsSql, "enums")}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(collationsSql(version), "collations")}${params?.roles ? `, (${roleSql(params.roles)}) AS "roles"` : ""}${params?.loadDefaultPrivileges ? `, (${defaultPrivilegesSql}) AS "defaultPrivileges"` : ""}${params?.loadGrants ? `, (${grantsSql}) AS "grants"` : ""}${params?.rls ? `, ${jsonAgg(policiesSql, "policies")}` : ""}`;
|
|
5072
5199
|
async function getDbVersion(db) {
|
|
5073
5200
|
const { rows: [{ version: versionString }] } = await db.query("SELECT version()");
|
|
5074
5201
|
return +versionString.match(/\d+/)[0];
|
|
@@ -5080,10 +5207,11 @@ async function introspectDbSchema(db, params) {
|
|
|
5080
5207
|
domain.checks = domain.checks?.filter((check) => check);
|
|
5081
5208
|
nullsToUndefined(domain);
|
|
5082
5209
|
}
|
|
5083
|
-
for (const table of raw.tables)
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5210
|
+
for (const table of raw.tables) processColumns(table.columns);
|
|
5211
|
+
for (const view of raw.views || []) processColumns(view.columns);
|
|
5212
|
+
for (const materializedView of raw.materializedViews || []) {
|
|
5213
|
+
nullsToUndefined(materializedView);
|
|
5214
|
+
processColumns(materializedView.columns);
|
|
5087
5215
|
}
|
|
5088
5216
|
const indexes = [];
|
|
5089
5217
|
const excludes = [];
|
|
@@ -5212,6 +5340,13 @@ const mapRawGrant = (raw) => {
|
|
|
5212
5340
|
if (grantablePrivileges.length) grant.grantablePrivileges = grantablePrivileges;
|
|
5213
5341
|
return grant;
|
|
5214
5342
|
};
|
|
5343
|
+
const processColumns = (columns) => {
|
|
5344
|
+
for (const column of columns) {
|
|
5345
|
+
nullsToUndefined(column);
|
|
5346
|
+
if (column.identity) nullsToUndefined(column.identity);
|
|
5347
|
+
if (column.compression) column.compression = column.compression === "p" ? "pglz" : "lz4";
|
|
5348
|
+
}
|
|
5349
|
+
};
|
|
5215
5350
|
const nullsToUndefined = (obj) => {
|
|
5216
5351
|
for (const key in obj) if (obj[key] === null) obj[key] = void 0;
|
|
5217
5352
|
};
|
|
@@ -5309,7 +5444,19 @@ const structureToAst = async (ctx, adapter, config) => {
|
|
|
5309
5444
|
tableSchema: table.schemaName === ctx.currentSchema ? void 0 : table.schemaName,
|
|
5310
5445
|
tableName: fkey.tableName
|
|
5311
5446
|
});
|
|
5312
|
-
for (const view of data.views) ast.push(viewToAst(ctx, data, domains, view));
|
|
5447
|
+
for (const view of data.views || []) ast.push(viewToAst(ctx, data, domains, view));
|
|
5448
|
+
for (const view of data.materializedViews || []) {
|
|
5449
|
+
ast.push(materializedViewToAst(ctx, data, domains, view));
|
|
5450
|
+
const indexes = materializedViewIndexesToAst(view, data);
|
|
5451
|
+
if (indexes.length) ast.push({
|
|
5452
|
+
type: "changeTable",
|
|
5453
|
+
schema: view.schemaName === ctx.currentSchema ? void 0 : view.schemaName,
|
|
5454
|
+
name: view.name,
|
|
5455
|
+
shape: {},
|
|
5456
|
+
add: { indexes },
|
|
5457
|
+
drop: {}
|
|
5458
|
+
});
|
|
5459
|
+
}
|
|
5313
5460
|
if (data.roles) for (const role of data.roles) ast.push({
|
|
5314
5461
|
type: "role",
|
|
5315
5462
|
action: "create",
|
|
@@ -5479,13 +5626,22 @@ const isColumnCheck = (it) => {
|
|
|
5479
5626
|
const viewToAst = (ctx, data, domains, view) => {
|
|
5480
5627
|
const shape = makeDbStructureColumnsShape(ctx, data, domains, view);
|
|
5481
5628
|
const options = {};
|
|
5482
|
-
if (view.isRecursive)
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5629
|
+
if (view.isRecursive) {
|
|
5630
|
+
options.recursive = true;
|
|
5631
|
+
options.columns = view.columns.map((column) => column.name);
|
|
5632
|
+
}
|
|
5633
|
+
if (view.with) for (const pair of view.with) {
|
|
5634
|
+
const [key, value] = pair.split("=");
|
|
5635
|
+
switch ((0, pqb_internal.toCamelCase)(key)) {
|
|
5636
|
+
case "checkOption":
|
|
5637
|
+
if (value === "LOCAL" || value === "CASCADED") options.checkOption = value;
|
|
5638
|
+
break;
|
|
5639
|
+
case "securityBarrier":
|
|
5640
|
+
options.securityBarrier = value === "true";
|
|
5641
|
+
break;
|
|
5642
|
+
case "securityInvoker":
|
|
5643
|
+
options.securityInvoker = value === "true";
|
|
5644
|
+
break;
|
|
5489
5645
|
}
|
|
5490
5646
|
}
|
|
5491
5647
|
return {
|
|
@@ -5499,11 +5655,40 @@ const viewToAst = (ctx, data, domains, view) => {
|
|
|
5499
5655
|
deps: view.deps
|
|
5500
5656
|
};
|
|
5501
5657
|
};
|
|
5658
|
+
const materializedViewToAst = (ctx, data, domains, view) => {
|
|
5659
|
+
const options = {};
|
|
5660
|
+
if (!view.isPopulated) options.withData = false;
|
|
5661
|
+
return {
|
|
5662
|
+
type: "materializedView",
|
|
5663
|
+
action: "create",
|
|
5664
|
+
schema: view.schemaName === ctx.currentSchema ? void 0 : view.schemaName,
|
|
5665
|
+
name: view.name,
|
|
5666
|
+
shape: makeDbStructureColumnsShape(ctx, data, domains, view),
|
|
5667
|
+
sql: (0, pqb_internal.raw)({ raw: view.sql }),
|
|
5668
|
+
options,
|
|
5669
|
+
deps: view.deps
|
|
5670
|
+
};
|
|
5671
|
+
};
|
|
5672
|
+
const materializedViewIndexesToAst = (view, data) => {
|
|
5673
|
+
return data.indexes.filter(filterByTableSchema(view.name, view.schemaName)).map((index) => ({
|
|
5674
|
+
columns: index.columns.map((column) => ({
|
|
5675
|
+
..."expression" in column ? { expression: column.expression } : { column: (0, pqb_internal.toCamelCase)(column.column) },
|
|
5676
|
+
collate: column.collate,
|
|
5677
|
+
opclass: column.opclass,
|
|
5678
|
+
order: column.order
|
|
5679
|
+
})),
|
|
5680
|
+
options: {
|
|
5681
|
+
...makeIndexOrExcludeOptions(view.name, index, "indexes"),
|
|
5682
|
+
include: index.include?.map(pqb_internal.toCamelCase)
|
|
5683
|
+
}
|
|
5684
|
+
}));
|
|
5685
|
+
};
|
|
5502
5686
|
const makeDbStructureColumnsShape = (ctx, data, domains, table, tableData) => {
|
|
5503
5687
|
const shape = {};
|
|
5504
5688
|
const checks = tableData ? getDbTableColumnsChecks(tableData) : void 0;
|
|
5689
|
+
const dbTable = tableData ? table : void 0;
|
|
5505
5690
|
for (const item of table.columns) {
|
|
5506
|
-
const [key, column] = dbColumnToAst(ctx, data, domains, table.name, item,
|
|
5691
|
+
const [key, column] = dbColumnToAst(ctx, data, domains, table.name, item, dbTable, tableData, checks);
|
|
5507
5692
|
shape[key] = column;
|
|
5508
5693
|
}
|
|
5509
5694
|
return shape;
|