rake-db 2.27.33 → 2.28.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 +8 -4
- package/dist/index.js +1529 -1506
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1531 -1509
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { isRawSQL, escapeForMigration, ArrayColumn, toSnakeCase, toCamelCase, DomainColumn, toArray, EnumColumn,
|
|
1
|
+
import { singleQuote, defaultSchemaConfig, makeColumnTypes, getStackTrace, isRawSQL, escapeForMigration, ArrayColumn, toSnakeCase, toCamelCase, DomainColumn, toArray, EnumColumn, snakeCaseKey, getColumnTypes, parseTableData, emptyObject, escapeString, tableDataMethods, setCurrentColumnName, consumeColumnName, Column, parseTableDataInput, UnknownColumn, setDefaultLanguage, deepCompare, raw, logParamToLogObject, createDbWithAdapter, getImportPath, pathToLog, emptyArray, colors, exhaustive, codeToString, addCode, quoteObjectKey, pushTableDataCode, rawSqlToCode, primaryKeyInnerToCode, indexInnerToCode, excludeInnerToCode, constraintInnerToCode, referencesArgsToCode, backtickQuote, TimestampTZColumn, TimestampColumn, makeColumnsByType, RawSql, CustomTypeColumn, assignDbDataToColumn, PostgisGeographyPointColumn } from 'pqb';
|
|
2
2
|
import path, { join } from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
4
|
import fs, { mkdir, writeFile, readdir, stat, readFile } from 'fs/promises';
|
|
5
5
|
|
|
6
6
|
class RakeDbError extends Error {
|
|
@@ -15,6 +15,177 @@ const clearChanges = () => {
|
|
|
15
15
|
const getCurrentChanges = () => currentChanges;
|
|
16
16
|
const pushChange = (change) => currentChanges.push(change);
|
|
17
17
|
|
|
18
|
+
const RAKE_DB_LOCK_KEY = "8582141715823621641";
|
|
19
|
+
const getFirstWordAndRest = (input) => {
|
|
20
|
+
const i = input.search(/(?=[A-Z])|[-_ ]/);
|
|
21
|
+
if (i !== -1) {
|
|
22
|
+
const restStart = input[i] === "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
|
|
23
|
+
const rest = input.slice(restStart);
|
|
24
|
+
return [input.slice(0, i), rest[0].toLowerCase() + rest.slice(1)];
|
|
25
|
+
} else {
|
|
26
|
+
return [input];
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const getTextAfterRegExp = (input, regex, length) => {
|
|
30
|
+
let i = input.search(regex);
|
|
31
|
+
if (i === -1) return;
|
|
32
|
+
if (input[i] === "-" || input[i] === "_" || input[i] === " ") i++;
|
|
33
|
+
i += length;
|
|
34
|
+
const start = input[i] == "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
|
|
35
|
+
const text = input.slice(start);
|
|
36
|
+
return text[0].toLowerCase() + text.slice(1);
|
|
37
|
+
};
|
|
38
|
+
const getTextAfterTo = (input) => {
|
|
39
|
+
return getTextAfterRegExp(input, /(To|-to|_to| to)[A-Z-_ ]/, 2);
|
|
40
|
+
};
|
|
41
|
+
const getTextAfterFrom = (input) => {
|
|
42
|
+
return getTextAfterRegExp(input, /(From|-from|_from| from)[A-Z-_ ]/, 4);
|
|
43
|
+
};
|
|
44
|
+
const joinColumns = (columns) => {
|
|
45
|
+
return columns.map((column) => `"${column}"`).join(", ");
|
|
46
|
+
};
|
|
47
|
+
const quoteWithSchema = ({
|
|
48
|
+
schema,
|
|
49
|
+
name
|
|
50
|
+
}) => quoteTable(schema, name);
|
|
51
|
+
const quoteTable = (schema, table) => schema ? `"${schema}"."${table}"` : `"${table}"`;
|
|
52
|
+
const getSchemaAndTableFromName = (name) => {
|
|
53
|
+
const i = name.indexOf(".");
|
|
54
|
+
return i !== -1 ? [name.slice(0, i), name.slice(i + 1)] : [void 0, name];
|
|
55
|
+
};
|
|
56
|
+
const quoteNameFromString = (string) => {
|
|
57
|
+
return quoteTable(...getSchemaAndTableFromName(string));
|
|
58
|
+
};
|
|
59
|
+
const quoteCustomType = (s) => {
|
|
60
|
+
const [schema, type] = getSchemaAndTableFromName(s);
|
|
61
|
+
return schema ? '"' + schema + '".' + type : type;
|
|
62
|
+
};
|
|
63
|
+
const quoteSchemaTable = (arg, excludeCurrentSchema) => {
|
|
64
|
+
return singleQuote(concatSchemaAndName(arg, excludeCurrentSchema));
|
|
65
|
+
};
|
|
66
|
+
const concatSchemaAndName = ({
|
|
67
|
+
schema,
|
|
68
|
+
name
|
|
69
|
+
}, excludeCurrentSchema) => {
|
|
70
|
+
return schema && schema !== excludeCurrentSchema ? `${schema}.${name}` : name;
|
|
71
|
+
};
|
|
72
|
+
const makePopulateEnumQuery = (item) => {
|
|
73
|
+
const [schema, name] = getSchemaAndTableFromName(item.enumName);
|
|
74
|
+
return {
|
|
75
|
+
text: `SELECT unnest(enum_range(NULL::${quoteTable(schema, name)}))::text`,
|
|
76
|
+
then(result) {
|
|
77
|
+
item.options.push(...result.rows.map(([value]) => value));
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const transaction = (adapter, fn) => {
|
|
82
|
+
return adapter.transaction(void 0, fn);
|
|
83
|
+
};
|
|
84
|
+
const queryLock = (trx) => trx.query(`SELECT pg_advisory_xact_lock('${RAKE_DB_LOCK_KEY}')`);
|
|
85
|
+
const getCliParam = (args, name) => {
|
|
86
|
+
if (args) {
|
|
87
|
+
const key = "--" + name;
|
|
88
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
89
|
+
const arg = args[i];
|
|
90
|
+
if (arg === key) return args[i + 1];
|
|
91
|
+
else if (arg.startsWith(key)) return arg.slice(key.length + 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const migrationConfigDefaults = {
|
|
98
|
+
schemaConfig: defaultSchemaConfig,
|
|
99
|
+
migrationsPath: path.join("src", "db", "migrations"),
|
|
100
|
+
migrationId: { serial: 4 },
|
|
101
|
+
migrationsTable: "schemaMigrations",
|
|
102
|
+
snakeCase: false,
|
|
103
|
+
commands: {},
|
|
104
|
+
log: true,
|
|
105
|
+
logger: console,
|
|
106
|
+
import() {
|
|
107
|
+
throw new Error(
|
|
108
|
+
"Add `import: (path) => import(path),` setting to `rakeDb` config"
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const ensureMigrationsPath = (config) => {
|
|
113
|
+
if (!config.migrationsPath) {
|
|
114
|
+
config.migrationsPath = migrationConfigDefaults.migrationsPath;
|
|
115
|
+
}
|
|
116
|
+
if (!path.isAbsolute(config.migrationsPath)) {
|
|
117
|
+
config.migrationsPath = path.resolve(
|
|
118
|
+
config.basePath,
|
|
119
|
+
config.migrationsPath
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return config;
|
|
123
|
+
};
|
|
124
|
+
const ensureBasePathAndDbScript = (config, intermediateCallers = 0) => {
|
|
125
|
+
if (config.basePath && config.dbScript) return config;
|
|
126
|
+
let filePath = getStackTrace()?.[3 + intermediateCallers]?.getFileName();
|
|
127
|
+
if (!filePath) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
"Failed to determine path to db script. Please set basePath option of rakeDb"
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
if (filePath.startsWith("file://")) {
|
|
133
|
+
filePath = fileURLToPath(filePath);
|
|
134
|
+
}
|
|
135
|
+
const ext = path.extname(filePath);
|
|
136
|
+
if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
config.basePath = path.dirname(filePath);
|
|
142
|
+
config.dbScript = path.basename(filePath);
|
|
143
|
+
return config;
|
|
144
|
+
};
|
|
145
|
+
const processRakeDbConfig = (config, args) => {
|
|
146
|
+
const result = { ...migrationConfigDefaults, ...config };
|
|
147
|
+
if (!result.log) {
|
|
148
|
+
delete result.logger;
|
|
149
|
+
}
|
|
150
|
+
ensureBasePathAndDbScript(result, 1);
|
|
151
|
+
ensureMigrationsPath(result);
|
|
152
|
+
if (!result.recurrentPath) {
|
|
153
|
+
result.recurrentPath = path.join(
|
|
154
|
+
result.migrationsPath,
|
|
155
|
+
"recurrent"
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if ("recurrentPath" in result && !path.isAbsolute(result.recurrentPath)) {
|
|
159
|
+
result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
|
|
160
|
+
}
|
|
161
|
+
if ("baseTable" in config && config.baseTable) {
|
|
162
|
+
const { types, snakeCase, language } = config.baseTable.prototype;
|
|
163
|
+
result.columnTypes = types || makeColumnTypes(defaultSchemaConfig);
|
|
164
|
+
if (snakeCase) result.snakeCase = true;
|
|
165
|
+
if (language) result.language = language;
|
|
166
|
+
} else {
|
|
167
|
+
const ct = "columnTypes" in config && config.columnTypes;
|
|
168
|
+
result.columnTypes = (typeof ct === "function" ? ct(
|
|
169
|
+
makeColumnTypes(defaultSchemaConfig)
|
|
170
|
+
) : ct) || makeColumnTypes(defaultSchemaConfig);
|
|
171
|
+
}
|
|
172
|
+
if (config.migrationId === "serial") {
|
|
173
|
+
result.migrationId = { serial: 4 };
|
|
174
|
+
}
|
|
175
|
+
const transaction = getCliParam(args, "transaction");
|
|
176
|
+
if (transaction) {
|
|
177
|
+
if (transaction !== "single" && transaction !== "per-migration") {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Unsupported transaction param ${transaction}, expected single or per-migration`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
result.transaction = transaction;
|
|
183
|
+
} else if (!result.transaction) {
|
|
184
|
+
result.transaction = "single";
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
};
|
|
188
|
+
|
|
18
189
|
const versionToString = (config, version) => config.migrationId === "timestamp" ? `${version}` : `${version}`.padStart(config.migrationId.serial, "0");
|
|
19
190
|
const columnTypeToSql = (item) => {
|
|
20
191
|
return item.data.isOfCustomType ? item instanceof DomainColumn ? quoteNameFromString(item.dataType) : quoteCustomType(item.toSQL()) : item.toSQL();
|
|
@@ -386,6 +557,17 @@ const cmpRawSql = (a, b) => {
|
|
|
386
557
|
const bValues = JSON.stringify(values);
|
|
387
558
|
return aSql === bSql && aValues === bValues;
|
|
388
559
|
};
|
|
560
|
+
const getMigrationsSchemaAndTable = (config) => {
|
|
561
|
+
const [tableSchema, table] = getSchemaAndTableFromName(
|
|
562
|
+
config.migrationsTable
|
|
563
|
+
);
|
|
564
|
+
const schema = tableSchema || (config.schema && config.schema !== "public" ? config.schema : void 0);
|
|
565
|
+
return { schema, table };
|
|
566
|
+
};
|
|
567
|
+
const migrationsSchemaTableSql = (config) => {
|
|
568
|
+
const { schema, table } = getMigrationsSchemaAndTable(config);
|
|
569
|
+
return `${schema ? `"${schema}".` : ""}"${table}"`;
|
|
570
|
+
};
|
|
389
571
|
|
|
390
572
|
const tableMethods = {
|
|
391
573
|
enum(name) {
|
|
@@ -1248,13 +1430,15 @@ const createView = async (migration, up, name, options, sql) => {
|
|
|
1248
1430
|
const query = astToQuery(ast);
|
|
1249
1431
|
await migration.adapter.arrays(interpolateSqlValues(query));
|
|
1250
1432
|
};
|
|
1251
|
-
const makeAst = (up,
|
|
1433
|
+
const makeAst = (up, fullName, options, sql) => {
|
|
1252
1434
|
if (typeof sql === "string") {
|
|
1253
1435
|
sql = raw({ raw: sql });
|
|
1254
1436
|
}
|
|
1437
|
+
const [schema, name] = getSchemaAndTableFromName(fullName);
|
|
1255
1438
|
return {
|
|
1256
1439
|
type: "view",
|
|
1257
1440
|
action: up ? "create" : "drop",
|
|
1441
|
+
schema,
|
|
1258
1442
|
name,
|
|
1259
1443
|
shape: {},
|
|
1260
1444
|
sql,
|
|
@@ -1266,12 +1450,13 @@ const astToQuery = (ast) => {
|
|
|
1266
1450
|
const values = [];
|
|
1267
1451
|
const sql = [];
|
|
1268
1452
|
const { options } = ast;
|
|
1453
|
+
const sqlName = `${ast.schema ? `"${ast.schema}".` : ""}"${ast.name}"`;
|
|
1269
1454
|
if (ast.action === "create") {
|
|
1270
1455
|
sql.push("CREATE");
|
|
1271
1456
|
if (options?.createOrReplace) sql.push("OR REPLACE");
|
|
1272
1457
|
if (options?.temporary) sql.push("TEMPORARY");
|
|
1273
1458
|
if (options?.recursive) sql.push("RECURSIVE");
|
|
1274
|
-
sql.push(`VIEW
|
|
1459
|
+
sql.push(`VIEW ${sqlName}`);
|
|
1275
1460
|
if (options?.columns) {
|
|
1276
1461
|
sql.push(
|
|
1277
1462
|
`(${options.columns.map((column) => `"${column}"`).join(", ")})`
|
|
@@ -1289,7 +1474,7 @@ const astToQuery = (ast) => {
|
|
|
1289
1474
|
} else {
|
|
1290
1475
|
sql.push("DROP VIEW");
|
|
1291
1476
|
if (options?.dropIfExists) sql.push(`IF EXISTS`);
|
|
1292
|
-
sql.push(
|
|
1477
|
+
sql.push(sqlName);
|
|
1293
1478
|
if (options?.dropMode) sql.push(options.dropMode);
|
|
1294
1479
|
}
|
|
1295
1480
|
return {
|
|
@@ -1300,7 +1485,6 @@ const astToQuery = (ast) => {
|
|
|
1300
1485
|
|
|
1301
1486
|
const createMigrationInterface = (tx, up, config) => {
|
|
1302
1487
|
const adapter = Object.create(tx);
|
|
1303
|
-
adapter.schema = adapter.getSchema() ?? "public";
|
|
1304
1488
|
const { query, arrays } = adapter;
|
|
1305
1489
|
const log = logParamToLogObject(config.logger || console, config.log);
|
|
1306
1490
|
adapter.query = (text, values) => {
|
|
@@ -2334,7 +2518,7 @@ const renameType = async (migration, from, to, kind) => {
|
|
|
2334
2518
|
}
|
|
2335
2519
|
if (ast.fromSchema !== ast.toSchema) {
|
|
2336
2520
|
await migration.adapter.query(
|
|
2337
|
-
`ALTER ${ast.kind} ${quoteTable(ast.fromSchema, ast.to)} SET SCHEMA "${ast.toSchema ?? migration.
|
|
2521
|
+
`ALTER ${ast.kind} ${quoteTable(ast.fromSchema, ast.to)} SET SCHEMA "${ast.toSchema ?? migration.options.schema ?? "public"}"`
|
|
2338
2522
|
);
|
|
2339
2523
|
}
|
|
2340
2524
|
};
|
|
@@ -2408,7 +2592,7 @@ const changeEnumValues = async (migration, enumName, fromValues, toValues) => {
|
|
|
2408
2592
|
);
|
|
2409
2593
|
};
|
|
2410
2594
|
const recreateEnum = async (migration, { schema, name }, values, errorMessage) => {
|
|
2411
|
-
const defaultSchema = migration.
|
|
2595
|
+
const defaultSchema = migration.options.schema ?? "public";
|
|
2412
2596
|
const quotedName = quoteTable(schema, name);
|
|
2413
2597
|
const relKinds = ["r", "m"];
|
|
2414
2598
|
const { rows: tables } = await migration.adapter.query(
|
|
@@ -2639,7 +2823,9 @@ ${arg === "timestamp" || digits !== 4 ? `Set \`migrationId\`: ${arg === "timesta
|
|
|
2639
2823
|
};
|
|
2640
2824
|
const renameMigrationVersionsInDb = async (config, adapter, values) => {
|
|
2641
2825
|
await adapter.arrays(
|
|
2642
|
-
`UPDATE
|
|
2826
|
+
`UPDATE ${migrationsSchemaTableSql(
|
|
2827
|
+
config
|
|
2828
|
+
)} AS t SET version = v.version FROM (VALUES ${values.map(
|
|
2643
2829
|
([oldVersion, , newVersion], i) => `('${oldVersion}', $${i + 1}, '${newVersion}')`
|
|
2644
2830
|
).join(
|
|
2645
2831
|
", "
|
|
@@ -2804,13 +2990,44 @@ function getDigitsPrefix(name) {
|
|
|
2804
2990
|
|
|
2805
2991
|
const saveMigratedVersion = async (db, version, name, config) => {
|
|
2806
2992
|
await db.silentArrays(
|
|
2807
|
-
`INSERT INTO
|
|
2993
|
+
`INSERT INTO ${migrationsSchemaTableSql(
|
|
2994
|
+
config
|
|
2995
|
+
)}(version, name) VALUES ($1, $2)`,
|
|
2808
2996
|
[version, name]
|
|
2809
2997
|
);
|
|
2810
2998
|
};
|
|
2999
|
+
const createMigrationsTable = async (db, config) => {
|
|
3000
|
+
const { schema } = getMigrationsSchemaAndTable(config);
|
|
3001
|
+
if (schema) {
|
|
3002
|
+
try {
|
|
3003
|
+
await db.query(`CREATE SCHEMA "${schema}"`);
|
|
3004
|
+
config.logger?.log(`Created schema ${schema}`);
|
|
3005
|
+
} catch (err) {
|
|
3006
|
+
if (err.code !== "42P06") {
|
|
3007
|
+
throw err;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
try {
|
|
3012
|
+
await db.query(
|
|
3013
|
+
`CREATE TABLE ${migrationsSchemaTableSql(
|
|
3014
|
+
config
|
|
3015
|
+
)} ( version TEXT NOT NULL, name TEXT NOT NULL )`
|
|
3016
|
+
);
|
|
3017
|
+
config.logger?.log("Created versions table");
|
|
3018
|
+
} catch (err) {
|
|
3019
|
+
if (err.code === "42P07") {
|
|
3020
|
+
config.logger?.log("Versions table exists");
|
|
3021
|
+
} else {
|
|
3022
|
+
throw err;
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
};
|
|
2811
3026
|
const deleteMigratedVersion = async (db, version, name, config) => {
|
|
2812
3027
|
const res = await db.silentArrays(
|
|
2813
|
-
`DELETE FROM
|
|
3028
|
+
`DELETE FROM ${migrationsSchemaTableSql(
|
|
3029
|
+
config
|
|
3030
|
+
)} WHERE version = $1 AND name = $2`,
|
|
2814
3031
|
[version, name]
|
|
2815
3032
|
);
|
|
2816
3033
|
if (res.rowCount === 0) {
|
|
@@ -2821,7 +3038,7 @@ class NoMigrationsTableError extends Error {
|
|
|
2821
3038
|
}
|
|
2822
3039
|
const getMigratedVersionsMap = async (ctx, adapter, config, renameTo) => {
|
|
2823
3040
|
try {
|
|
2824
|
-
const table =
|
|
3041
|
+
const table = migrationsSchemaTableSql(config);
|
|
2825
3042
|
const result = await adapter.arrays(
|
|
2826
3043
|
`SELECT * FROM ${table} ORDER BY version`
|
|
2827
3044
|
);
|
|
@@ -2897,33 +3114,6 @@ async function renameMigrations(config, trx, versions, renameTo) {
|
|
|
2897
3114
|
return updatedVersions;
|
|
2898
3115
|
}
|
|
2899
3116
|
|
|
2900
|
-
const createMigrationsTable = async (db, config) => {
|
|
2901
|
-
const { schema } = db;
|
|
2902
|
-
if (schema && schema !== "public") {
|
|
2903
|
-
try {
|
|
2904
|
-
await db.query(`CREATE SCHEMA "${schema}"`);
|
|
2905
|
-
config.logger?.log(`Created schema ${schema}`);
|
|
2906
|
-
} catch (err) {
|
|
2907
|
-
if (err.code !== "42P06") {
|
|
2908
|
-
throw err;
|
|
2909
|
-
}
|
|
2910
|
-
}
|
|
2911
|
-
}
|
|
2912
|
-
try {
|
|
2913
|
-
await db.query(
|
|
2914
|
-
`CREATE TABLE "${config.migrationsTable}" ( version TEXT NOT NULL, name TEXT NOT NULL )`
|
|
2915
|
-
);
|
|
2916
|
-
config.logger?.log("Created versions table");
|
|
2917
|
-
} catch (err) {
|
|
2918
|
-
if (err.code === "42P07") {
|
|
2919
|
-
config.logger?.log("Versions table exists");
|
|
2920
|
-
} else {
|
|
2921
|
-
throw err;
|
|
2922
|
-
}
|
|
2923
|
-
}
|
|
2924
|
-
};
|
|
2925
|
-
|
|
2926
|
-
const RAKE_DB_LOCK_KEY = "8582141715823621641";
|
|
2927
3117
|
const transactionIfSingle = (params, fn) => {
|
|
2928
3118
|
return params.config.transaction === "single" ? transaction(params.adapter, fn) : fn(params.adapter);
|
|
2929
3119
|
};
|
|
@@ -3182,199 +3372,29 @@ const changeMigratedVersion = async (adapter, up, file, config) => {
|
|
|
3182
3372
|
);
|
|
3183
3373
|
};
|
|
3184
3374
|
|
|
3185
|
-
const
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3375
|
+
const ESC = "\x1B";
|
|
3376
|
+
const CSI = `${ESC}[`;
|
|
3377
|
+
const cursorShow = `${CSI}?25h`;
|
|
3378
|
+
const cursorHide = `${CSI}?25l`;
|
|
3379
|
+
const { stdin, stdout } = process;
|
|
3380
|
+
const visibleChars = (s) => s.replace(
|
|
3381
|
+
// eslint-disable-next-line no-control-regex
|
|
3382
|
+
/[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))/g,
|
|
3383
|
+
""
|
|
3384
|
+
).length;
|
|
3385
|
+
const clear = (text) => {
|
|
3386
|
+
const rows = text.split(/\r?\n/).reduce(
|
|
3387
|
+
(rows2, line) => rows2 + 1 + Math.floor(Math.max(visibleChars(line) - 1, 0) / stdout.columns),
|
|
3388
|
+
0
|
|
3389
|
+
);
|
|
3390
|
+
let clear2 = "";
|
|
3391
|
+
for (let i = 0; i < rows; i++) {
|
|
3392
|
+
clear2 += `${CSI}2K`;
|
|
3393
|
+
if (i < rows - 1) {
|
|
3394
|
+
clear2 += `${CSI}${i < rows - 1 ? "1A" : "G"}`;
|
|
3395
|
+
}
|
|
3193
3396
|
}
|
|
3194
|
-
|
|
3195
|
-
const getTextAfterRegExp = (input, regex, length) => {
|
|
3196
|
-
let i = input.search(regex);
|
|
3197
|
-
if (i === -1) return;
|
|
3198
|
-
if (input[i] === "-" || input[i] === "_" || input[i] === " ") i++;
|
|
3199
|
-
i += length;
|
|
3200
|
-
const start = input[i] == "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
|
|
3201
|
-
const text = input.slice(start);
|
|
3202
|
-
return text[0].toLowerCase() + text.slice(1);
|
|
3203
|
-
};
|
|
3204
|
-
const getTextAfterTo = (input) => {
|
|
3205
|
-
return getTextAfterRegExp(input, /(To|-to|_to| to)[A-Z-_ ]/, 2);
|
|
3206
|
-
};
|
|
3207
|
-
const getTextAfterFrom = (input) => {
|
|
3208
|
-
return getTextAfterRegExp(input, /(From|-from|_from| from)[A-Z-_ ]/, 4);
|
|
3209
|
-
};
|
|
3210
|
-
const joinColumns = (columns) => {
|
|
3211
|
-
return columns.map((column) => `"${column}"`).join(", ");
|
|
3212
|
-
};
|
|
3213
|
-
const quoteWithSchema = ({
|
|
3214
|
-
schema,
|
|
3215
|
-
name
|
|
3216
|
-
}) => quoteTable(schema, name);
|
|
3217
|
-
const quoteTable = (schema, table) => schema ? `"${schema}"."${table}"` : `"${table}"`;
|
|
3218
|
-
const getSchemaAndTableFromName = (name) => {
|
|
3219
|
-
const i = name.indexOf(".");
|
|
3220
|
-
return i !== -1 ? [name.slice(0, i), name.slice(i + 1)] : [void 0, name];
|
|
3221
|
-
};
|
|
3222
|
-
const quoteNameFromString = (string) => {
|
|
3223
|
-
return quoteTable(...getSchemaAndTableFromName(string));
|
|
3224
|
-
};
|
|
3225
|
-
const quoteCustomType = (s) => {
|
|
3226
|
-
const [schema, type] = getSchemaAndTableFromName(s);
|
|
3227
|
-
return schema ? '"' + schema + '".' + type : type;
|
|
3228
|
-
};
|
|
3229
|
-
const quoteSchemaTable = (arg, excludeCurrentSchema) => {
|
|
3230
|
-
return singleQuote(concatSchemaAndName(arg, excludeCurrentSchema));
|
|
3231
|
-
};
|
|
3232
|
-
const concatSchemaAndName = ({
|
|
3233
|
-
schema,
|
|
3234
|
-
name
|
|
3235
|
-
}, excludeCurrentSchema) => {
|
|
3236
|
-
return schema && schema !== excludeCurrentSchema ? `${schema}.${name}` : name;
|
|
3237
|
-
};
|
|
3238
|
-
const makePopulateEnumQuery = (item) => {
|
|
3239
|
-
const [schema, name] = getSchemaAndTableFromName(item.enumName);
|
|
3240
|
-
return {
|
|
3241
|
-
text: `SELECT unnest(enum_range(NULL::${quoteTable(schema, name)}))::text`,
|
|
3242
|
-
then(result) {
|
|
3243
|
-
item.options.push(...result.rows.map(([value]) => value));
|
|
3244
|
-
}
|
|
3245
|
-
};
|
|
3246
|
-
};
|
|
3247
|
-
const transaction = (adapter, fn) => {
|
|
3248
|
-
return adapter.transaction(void 0, fn);
|
|
3249
|
-
};
|
|
3250
|
-
const queryLock = (trx) => trx.query(`SELECT pg_advisory_xact_lock('${RAKE_DB_LOCK_KEY}')`);
|
|
3251
|
-
const getCliParam = (args, name) => {
|
|
3252
|
-
if (args) {
|
|
3253
|
-
const key = "--" + name;
|
|
3254
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
3255
|
-
const arg = args[i];
|
|
3256
|
-
if (arg === key) return args[i + 1];
|
|
3257
|
-
else if (arg.startsWith(key)) return arg.slice(key.length + 1);
|
|
3258
|
-
}
|
|
3259
|
-
}
|
|
3260
|
-
return;
|
|
3261
|
-
};
|
|
3262
|
-
|
|
3263
|
-
const migrationConfigDefaults = {
|
|
3264
|
-
schemaConfig: defaultSchemaConfig,
|
|
3265
|
-
migrationsPath: path.join("src", "db", "migrations"),
|
|
3266
|
-
migrationId: { serial: 4 },
|
|
3267
|
-
migrationsTable: "schemaMigrations",
|
|
3268
|
-
snakeCase: false,
|
|
3269
|
-
commands: {},
|
|
3270
|
-
log: true,
|
|
3271
|
-
logger: console,
|
|
3272
|
-
import() {
|
|
3273
|
-
throw new Error(
|
|
3274
|
-
"Add `import: (path) => import(path),` setting to `rakeDb` config"
|
|
3275
|
-
);
|
|
3276
|
-
}
|
|
3277
|
-
};
|
|
3278
|
-
const ensureMigrationsPath = (config) => {
|
|
3279
|
-
if (!config.migrationsPath) {
|
|
3280
|
-
config.migrationsPath = migrationConfigDefaults.migrationsPath;
|
|
3281
|
-
}
|
|
3282
|
-
if (!path.isAbsolute(config.migrationsPath)) {
|
|
3283
|
-
config.migrationsPath = path.resolve(
|
|
3284
|
-
config.basePath,
|
|
3285
|
-
config.migrationsPath
|
|
3286
|
-
);
|
|
3287
|
-
}
|
|
3288
|
-
return config;
|
|
3289
|
-
};
|
|
3290
|
-
const ensureBasePathAndDbScript = (config, intermediateCallers = 0) => {
|
|
3291
|
-
if (config.basePath && config.dbScript) return config;
|
|
3292
|
-
let filePath = getStackTrace()?.[3 + intermediateCallers]?.getFileName();
|
|
3293
|
-
if (!filePath) {
|
|
3294
|
-
throw new Error(
|
|
3295
|
-
"Failed to determine path to db script. Please set basePath option of rakeDb"
|
|
3296
|
-
);
|
|
3297
|
-
}
|
|
3298
|
-
if (filePath.startsWith("file://")) {
|
|
3299
|
-
filePath = fileURLToPath(filePath);
|
|
3300
|
-
}
|
|
3301
|
-
const ext = path.extname(filePath);
|
|
3302
|
-
if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
|
|
3303
|
-
throw new Error(
|
|
3304
|
-
`Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
|
|
3305
|
-
);
|
|
3306
|
-
}
|
|
3307
|
-
config.basePath = path.dirname(filePath);
|
|
3308
|
-
config.dbScript = path.basename(filePath);
|
|
3309
|
-
return config;
|
|
3310
|
-
};
|
|
3311
|
-
const processRakeDbConfig = (config, args) => {
|
|
3312
|
-
const result = { ...migrationConfigDefaults, ...config };
|
|
3313
|
-
if (!result.log) {
|
|
3314
|
-
delete result.logger;
|
|
3315
|
-
}
|
|
3316
|
-
ensureBasePathAndDbScript(result, 1);
|
|
3317
|
-
ensureMigrationsPath(result);
|
|
3318
|
-
if (!result.recurrentPath) {
|
|
3319
|
-
result.recurrentPath = path.join(
|
|
3320
|
-
result.migrationsPath,
|
|
3321
|
-
"recurrent"
|
|
3322
|
-
);
|
|
3323
|
-
}
|
|
3324
|
-
if ("recurrentPath" in result && !path.isAbsolute(result.recurrentPath)) {
|
|
3325
|
-
result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
|
|
3326
|
-
}
|
|
3327
|
-
if ("baseTable" in config && config.baseTable) {
|
|
3328
|
-
const { types, snakeCase, language } = config.baseTable.prototype;
|
|
3329
|
-
result.columnTypes = types || makeColumnTypes(defaultSchemaConfig);
|
|
3330
|
-
if (snakeCase) result.snakeCase = true;
|
|
3331
|
-
if (language) result.language = language;
|
|
3332
|
-
} else {
|
|
3333
|
-
const ct = "columnTypes" in config && config.columnTypes;
|
|
3334
|
-
result.columnTypes = (typeof ct === "function" ? ct(
|
|
3335
|
-
makeColumnTypes(defaultSchemaConfig)
|
|
3336
|
-
) : ct) || makeColumnTypes(defaultSchemaConfig);
|
|
3337
|
-
}
|
|
3338
|
-
if (config.migrationId === "serial") {
|
|
3339
|
-
result.migrationId = { serial: 4 };
|
|
3340
|
-
}
|
|
3341
|
-
const transaction = getCliParam(args, "transaction");
|
|
3342
|
-
if (transaction) {
|
|
3343
|
-
if (transaction !== "single" && transaction !== "per-migration") {
|
|
3344
|
-
throw new Error(
|
|
3345
|
-
`Unsupported transaction param ${transaction}, expected single or per-migration`
|
|
3346
|
-
);
|
|
3347
|
-
}
|
|
3348
|
-
result.transaction = transaction;
|
|
3349
|
-
} else if (!result.transaction) {
|
|
3350
|
-
result.transaction = "single";
|
|
3351
|
-
}
|
|
3352
|
-
return result;
|
|
3353
|
-
};
|
|
3354
|
-
|
|
3355
|
-
const ESC = "\x1B";
|
|
3356
|
-
const CSI = `${ESC}[`;
|
|
3357
|
-
const cursorShow = `${CSI}?25h`;
|
|
3358
|
-
const cursorHide = `${CSI}?25l`;
|
|
3359
|
-
const { stdin, stdout } = process;
|
|
3360
|
-
const visibleChars = (s) => s.replace(
|
|
3361
|
-
// eslint-disable-next-line no-control-regex
|
|
3362
|
-
/[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))/g,
|
|
3363
|
-
""
|
|
3364
|
-
).length;
|
|
3365
|
-
const clear = (text) => {
|
|
3366
|
-
const rows = text.split(/\r?\n/).reduce(
|
|
3367
|
-
(rows2, line) => rows2 + 1 + Math.floor(Math.max(visibleChars(line) - 1, 0) / stdout.columns),
|
|
3368
|
-
0
|
|
3369
|
-
);
|
|
3370
|
-
let clear2 = "";
|
|
3371
|
-
for (let i = 0; i < rows; i++) {
|
|
3372
|
-
clear2 += `${CSI}2K`;
|
|
3373
|
-
if (i < rows - 1) {
|
|
3374
|
-
clear2 += `${CSI}${i < rows - 1 ? "1A" : "G"}`;
|
|
3375
|
-
}
|
|
3376
|
-
}
|
|
3377
|
-
return clear2;
|
|
3397
|
+
return clear2;
|
|
3378
3398
|
};
|
|
3379
3399
|
const prompt = async ({
|
|
3380
3400
|
render,
|
|
@@ -3684,962 +3704,6 @@ const readdirRecursive = async (dirPath, cb) => {
|
|
|
3684
3704
|
);
|
|
3685
3705
|
};
|
|
3686
3706
|
|
|
3687
|
-
const filterSchema = (table) => `${table} !~ '^pg_' AND ${table} != 'information_schema'`;
|
|
3688
|
-
const jsonAgg = (sql2, as) => `(SELECT coalesce(json_agg(t.*), '[]') FROM (${sql2}) t) AS "${as}"`;
|
|
3689
|
-
const columnsSql = ({
|
|
3690
|
-
schema,
|
|
3691
|
-
table,
|
|
3692
|
-
join = "",
|
|
3693
|
-
where
|
|
3694
|
-
}) => `SELECT
|
|
3695
|
-
${schema}.nspname "schemaName",
|
|
3696
|
-
${table}.relname "tableName",
|
|
3697
|
-
a.attname "name",
|
|
3698
|
-
t.typname "type",
|
|
3699
|
-
tn.nspname "typeSchema",
|
|
3700
|
-
a.attndims "arrayDims",
|
|
3701
|
-
information_schema._pg_char_max_length(tt.id, tt.mod) "maxChars",
|
|
3702
|
-
information_schema._pg_numeric_precision(tt.id, tt.mod) "numericPrecision",
|
|
3703
|
-
information_schema._pg_numeric_scale(tt.id,tt.mod) "numericScale",
|
|
3704
|
-
information_schema._pg_datetime_precision(tt.id,tt.mod) "dateTimePrecision",
|
|
3705
|
-
CAST(
|
|
3706
|
-
CASE WHEN a.attgenerated = ''
|
|
3707
|
-
THEN pg_get_expr(ad.adbin, ad.adrelid)
|
|
3708
|
-
END AS information_schema.character_data
|
|
3709
|
-
) AS "default",
|
|
3710
|
-
NOT (a.attnotnull OR (t.typtype = 'd' AND t.typnotnull)) AS "isNullable",
|
|
3711
|
-
co.collname AS "collate",
|
|
3712
|
-
NULLIF(a.attcompression, '') AS compression,
|
|
3713
|
-
pgd.description AS "comment",
|
|
3714
|
-
(
|
|
3715
|
-
CASE WHEN a.attidentity IN ('a', 'd') THEN (
|
|
3716
|
-
json_build_object(
|
|
3717
|
-
'always',
|
|
3718
|
-
a.attidentity = 'a',
|
|
3719
|
-
'start',
|
|
3720
|
-
seq.seqstart,
|
|
3721
|
-
'increment',
|
|
3722
|
-
seq.seqincrement,
|
|
3723
|
-
'min',
|
|
3724
|
-
nullif(seq.seqmin, 1),
|
|
3725
|
-
'max',
|
|
3726
|
-
nullif(seq.seqmax, (
|
|
3727
|
-
CASE t.typname
|
|
3728
|
-
WHEN 'int2' THEN 32767
|
|
3729
|
-
WHEN 'int4' THEN 2147483647
|
|
3730
|
-
WHEN 'int8' THEN 9223372036854775807
|
|
3731
|
-
ELSE NULL
|
|
3732
|
-
END
|
|
3733
|
-
)),
|
|
3734
|
-
'cache',
|
|
3735
|
-
seq.seqcache,
|
|
3736
|
-
'cycle',
|
|
3737
|
-
seq.seqcycle
|
|
3738
|
-
)
|
|
3739
|
-
) END
|
|
3740
|
-
) "identity",
|
|
3741
|
-
ext.extname "extension",
|
|
3742
|
-
a.atttypmod "typmod"
|
|
3743
|
-
FROM pg_attribute a
|
|
3744
|
-
${join}
|
|
3745
|
-
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
|
3746
|
-
JOIN pg_type t
|
|
3747
|
-
ON t.oid = (
|
|
3748
|
-
CASE WHEN a.attndims = 0
|
|
3749
|
-
THEN a.atttypid
|
|
3750
|
-
ELSE (SELECT t.typelem FROM pg_type t WHERE t.oid = a.atttypid)
|
|
3751
|
-
END
|
|
3752
|
-
)
|
|
3753
|
-
JOIN LATERAL (
|
|
3754
|
-
SELECT
|
|
3755
|
-
CASE WHEN t.typtype = 'd' THEN t.typbasetype ELSE t.oid END id,
|
|
3756
|
-
CASE WHEN t.typtype = 'd' THEN t.typtypmod ELSE a.atttypmod END mod
|
|
3757
|
-
) tt ON true
|
|
3758
|
-
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
|
3759
|
-
LEFT JOIN (pg_collation co JOIN pg_namespace nco ON (co.collnamespace = nco.oid))
|
|
3760
|
-
ON a.attcollation = co.oid AND (nco.nspname, co.collname) <> ('pg_catalog', 'default')
|
|
3761
|
-
LEFT JOIN pg_catalog.pg_description pgd
|
|
3762
|
-
ON pgd.objoid = a.attrelid
|
|
3763
|
-
AND pgd.objsubid = a.attnum
|
|
3764
|
-
LEFT JOIN (pg_depend dep JOIN pg_sequence seq ON (dep.classid = 'pg_class'::regclass AND dep.objid = seq.seqrelid AND dep.deptype = 'i'))
|
|
3765
|
-
ON (dep.refclassid = 'pg_class'::regclass AND dep.refobjid = ${table}.oid AND dep.refobjsubid = a.attnum)
|
|
3766
|
-
LEFT JOIN pg_depend d ON d.objid = t.oid AND d.classid = 'pg_type'::regclass AND d.deptype = 'e'
|
|
3767
|
-
LEFT JOIN pg_extension ext ON ext.oid = d.refobjid
|
|
3768
|
-
WHERE a.attnum > 0
|
|
3769
|
-
AND NOT a.attisdropped
|
|
3770
|
-
AND ${where}
|
|
3771
|
-
ORDER BY a.attnum`;
|
|
3772
|
-
const schemasSql = `SELECT coalesce(json_agg(nspname ORDER BY nspname), '[]')
|
|
3773
|
-
FROM pg_catalog.pg_namespace n
|
|
3774
|
-
WHERE ${filterSchema("nspname")}`;
|
|
3775
|
-
const tablesSql = `SELECT
|
|
3776
|
-
nspname AS "schemaName",
|
|
3777
|
-
relname AS "name",
|
|
3778
|
-
obj_description(c.oid) AS comment,
|
|
3779
|
-
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
3780
|
-
schema: "n",
|
|
3781
|
-
table: "c",
|
|
3782
|
-
where: "a.attrelid = c.oid"
|
|
3783
|
-
})}) t) AS "columns"
|
|
3784
|
-
FROM pg_class c
|
|
3785
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = relnamespace
|
|
3786
|
-
WHERE (relkind = 'r' OR relkind = 'p')
|
|
3787
|
-
AND ${filterSchema("nspname")}
|
|
3788
|
-
ORDER BY relname`;
|
|
3789
|
-
const viewsSql = `SELECT
|
|
3790
|
-
nc.nspname AS "schemaName",
|
|
3791
|
-
c.relname AS "name",
|
|
3792
|
-
(
|
|
3793
|
-
SELECT COALESCE(json_agg(t.*), '[]')
|
|
3794
|
-
FROM (
|
|
3795
|
-
SELECT
|
|
3796
|
-
ns.nspname AS "schemaName",
|
|
3797
|
-
obj.relname AS "name"
|
|
3798
|
-
FROM pg_class obj
|
|
3799
|
-
JOIN pg_depend dep ON dep.refobjid = obj.oid
|
|
3800
|
-
JOIN pg_rewrite rew ON rew.oid = dep.objid
|
|
3801
|
-
JOIN pg_namespace ns ON ns.oid = obj.relnamespace
|
|
3802
|
-
WHERE rew.ev_class = c.oid AND obj.oid <> c.oid
|
|
3803
|
-
) t
|
|
3804
|
-
) "deps",
|
|
3805
|
-
right(substring(r.ev_action from ':hasRecursive w'), 1)::bool AS "isRecursive",
|
|
3806
|
-
array_to_json(c.reloptions) AS "with",
|
|
3807
|
-
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
3808
|
-
schema: "nc",
|
|
3809
|
-
table: "c",
|
|
3810
|
-
where: "a.attrelid = c.oid"
|
|
3811
|
-
})}) t) AS "columns",
|
|
3812
|
-
pg_get_viewdef(c.oid) AS "sql"
|
|
3813
|
-
FROM pg_namespace nc
|
|
3814
|
-
JOIN pg_class c
|
|
3815
|
-
ON nc.oid = c.relnamespace
|
|
3816
|
-
AND c.relkind = 'v'
|
|
3817
|
-
AND c.relpersistence != 't'
|
|
3818
|
-
JOIN pg_rewrite r ON r.ev_class = c.oid
|
|
3819
|
-
WHERE ${filterSchema("nc.nspname")}
|
|
3820
|
-
ORDER BY c.relname`;
|
|
3821
|
-
const indexesSql = `SELECT
|
|
3822
|
-
n.nspname "schemaName",
|
|
3823
|
-
t.relname "tableName",
|
|
3824
|
-
ic.relname "name",
|
|
3825
|
-
am.amname AS "using",
|
|
3826
|
-
i.indisunique "unique",
|
|
3827
|
-
(
|
|
3828
|
-
SELECT json_agg(
|
|
3829
|
-
(
|
|
3830
|
-
CASE WHEN t.e = 0
|
|
3831
|
-
THEN jsonb_build_object('expression', pg_get_indexdef(i.indexrelid, t.i::int4, false))
|
|
3832
|
-
ELSE jsonb_build_object('column', (
|
|
3833
|
-
(
|
|
3834
|
-
SELECT attname
|
|
3835
|
-
FROM pg_catalog.pg_attribute
|
|
3836
|
-
WHERE attrelid = i.indrelid
|
|
3837
|
-
AND attnum = t.e
|
|
3838
|
-
)
|
|
3839
|
-
))
|
|
3840
|
-
END
|
|
3841
|
-
) || (
|
|
3842
|
-
CASE WHEN i.indcollation[t.i - 1] = 0
|
|
3843
|
-
THEN '{}'::jsonb
|
|
3844
|
-
ELSE (
|
|
3845
|
-
SELECT (
|
|
3846
|
-
CASE WHEN collname = 'default'
|
|
3847
|
-
THEN '{}'::jsonb
|
|
3848
|
-
ELSE jsonb_build_object('collate', collname)
|
|
3849
|
-
END
|
|
3850
|
-
)
|
|
3851
|
-
FROM pg_catalog.pg_collation
|
|
3852
|
-
WHERE oid = i.indcollation[t.i - 1]
|
|
3853
|
-
)
|
|
3854
|
-
END
|
|
3855
|
-
) || (
|
|
3856
|
-
SELECT
|
|
3857
|
-
CASE WHEN opcdefault AND attoptions IS NULL
|
|
3858
|
-
THEN '{}'::jsonb
|
|
3859
|
-
ELSE jsonb_build_object(
|
|
3860
|
-
'opclass', opcname || COALESCE('(' || array_to_string(attoptions, ', ') || ')', '')
|
|
3861
|
-
)
|
|
3862
|
-
END
|
|
3863
|
-
FROM pg_opclass
|
|
3864
|
-
LEFT JOIN pg_attribute
|
|
3865
|
-
ON attrelid = i.indexrelid
|
|
3866
|
-
AND attnum = t.i
|
|
3867
|
-
WHERE oid = i.indclass[t.i - 1]
|
|
3868
|
-
) || (
|
|
3869
|
-
CASE WHEN i.indoption[t.i - 1] = 0
|
|
3870
|
-
THEN '{}'::jsonb
|
|
3871
|
-
ELSE jsonb_build_object(
|
|
3872
|
-
'order',
|
|
3873
|
-
CASE
|
|
3874
|
-
WHEN i.indoption[t.i - 1] = 1 THEN 'DESC NULLS LAST'
|
|
3875
|
-
WHEN i.indoption[t.i - 1] = 2 THEN 'ASC NULLS FIRST'
|
|
3876
|
-
WHEN i.indoption[t.i - 1] = 3 THEN 'DESC'
|
|
3877
|
-
ELSE NULL
|
|
3878
|
-
END
|
|
3879
|
-
)
|
|
3880
|
-
END
|
|
3881
|
-
)
|
|
3882
|
-
)
|
|
3883
|
-
FROM unnest(i.indkey[:indnkeyatts - 1]) WITH ORDINALITY AS t(e, i)
|
|
3884
|
-
) "columns",
|
|
3885
|
-
(
|
|
3886
|
-
SELECT json_agg(
|
|
3887
|
-
(
|
|
3888
|
-
SELECT attname
|
|
3889
|
-
FROM pg_catalog.pg_attribute
|
|
3890
|
-
WHERE attrelid = i.indrelid
|
|
3891
|
-
AND attnum = j.e
|
|
3892
|
-
)
|
|
3893
|
-
)
|
|
3894
|
-
FROM unnest(i.indkey[indnkeyatts:]) AS j(e)
|
|
3895
|
-
) AS "include",
|
|
3896
|
-
(to_jsonb(i.*)->'indnullsnotdistinct')::bool AS "nullsNotDistinct",
|
|
3897
|
-
NULLIF(pg_catalog.array_to_string(
|
|
3898
|
-
ic.reloptions || array(SELECT 'toast.' || x FROM pg_catalog.unnest(tc.reloptions) x),
|
|
3899
|
-
', '
|
|
3900
|
-
), '') AS "with",
|
|
3901
|
-
(
|
|
3902
|
-
SELECT tablespace
|
|
3903
|
-
FROM pg_indexes
|
|
3904
|
-
WHERE schemaname = n.nspname
|
|
3905
|
-
AND indexname = ic.relname
|
|
3906
|
-
) AS tablespace,
|
|
3907
|
-
pg_get_expr(i.indpred, i.indrelid) AS "where",
|
|
3908
|
-
(
|
|
3909
|
-
CASE i.indisexclusion WHEN true
|
|
3910
|
-
THEN (
|
|
3911
|
-
SELECT json_agg(o.oprname)
|
|
3912
|
-
FROM pg_catalog.pg_constraint c, LATERAL unnest(c.conexclop) op_oid
|
|
3913
|
-
JOIN pg_operator o ON o.oid = op_oid
|
|
3914
|
-
WHERE c.conindid = ic.oid
|
|
3915
|
-
)
|
|
3916
|
-
END
|
|
3917
|
-
) "exclude"
|
|
3918
|
-
FROM pg_index i
|
|
3919
|
-
JOIN pg_class t ON t.oid = i.indrelid
|
|
3920
|
-
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
3921
|
-
JOIN pg_class ic ON ic.oid = i.indexrelid
|
|
3922
|
-
JOIN pg_am am ON am.oid = ic.relam
|
|
3923
|
-
LEFT JOIN pg_catalog.pg_class tc ON (ic.reltoastrelid = tc.oid)
|
|
3924
|
-
WHERE ${filterSchema("n.nspname")}
|
|
3925
|
-
AND NOT i.indisprimary
|
|
3926
|
-
ORDER BY ic.relname`;
|
|
3927
|
-
const constraintsSql = `SELECT
|
|
3928
|
-
s.nspname AS "schemaName",
|
|
3929
|
-
t.relname AS "tableName",
|
|
3930
|
-
c.conname AS "name",
|
|
3931
|
-
(
|
|
3932
|
-
SELECT json_agg(ccu.column_name)
|
|
3933
|
-
FROM information_schema.constraint_column_usage ccu
|
|
3934
|
-
WHERE contype = 'p'
|
|
3935
|
-
AND ccu.constraint_name = c.conname
|
|
3936
|
-
AND ccu.table_schema = s.nspname
|
|
3937
|
-
) AS "primaryKey",
|
|
3938
|
-
(
|
|
3939
|
-
SELECT
|
|
3940
|
-
json_build_object(
|
|
3941
|
-
'foreignSchema',
|
|
3942
|
-
fs.nspname,
|
|
3943
|
-
'foreignTable',
|
|
3944
|
-
ft.relname,
|
|
3945
|
-
'columns',
|
|
3946
|
-
(
|
|
3947
|
-
SELECT json_agg(ccu.column_name)
|
|
3948
|
-
FROM information_schema.key_column_usage ccu
|
|
3949
|
-
WHERE ccu.constraint_name = c.conname
|
|
3950
|
-
AND ccu.table_schema = cs.nspname
|
|
3951
|
-
),
|
|
3952
|
-
'foreignColumns',
|
|
3953
|
-
(
|
|
3954
|
-
SELECT json_agg(ccu.column_name)
|
|
3955
|
-
FROM information_schema.constraint_column_usage ccu
|
|
3956
|
-
WHERE ccu.constraint_name = c.conname
|
|
3957
|
-
AND ccu.table_schema = cs.nspname
|
|
3958
|
-
),
|
|
3959
|
-
'match',
|
|
3960
|
-
c.confmatchtype,
|
|
3961
|
-
'onUpdate',
|
|
3962
|
-
c.confupdtype,
|
|
3963
|
-
'onDelete',
|
|
3964
|
-
c.confdeltype
|
|
3965
|
-
)
|
|
3966
|
-
FROM pg_class ft
|
|
3967
|
-
JOIN pg_catalog.pg_namespace fs ON fs.oid = ft.relnamespace
|
|
3968
|
-
JOIN pg_catalog.pg_namespace cs ON cs.oid = c.connamespace
|
|
3969
|
-
WHERE contype = 'f' AND ft.oid = confrelid
|
|
3970
|
-
) AS "references",
|
|
3971
|
-
(
|
|
3972
|
-
SELECT
|
|
3973
|
-
CASE conbin IS NULL
|
|
3974
|
-
WHEN false THEN
|
|
3975
|
-
json_build_object(
|
|
3976
|
-
'columns',
|
|
3977
|
-
json_agg(ccu.column_name),
|
|
3978
|
-
'expression',
|
|
3979
|
-
pg_get_expr(conbin, conrelid)
|
|
3980
|
-
)
|
|
3981
|
-
END
|
|
3982
|
-
FROM information_schema.constraint_column_usage ccu
|
|
3983
|
-
WHERE conbin IS NOT NULL
|
|
3984
|
-
AND ccu.constraint_name = c.conname
|
|
3985
|
-
AND ccu.table_schema = s.nspname
|
|
3986
|
-
) AS "check"
|
|
3987
|
-
FROM pg_catalog.pg_constraint c
|
|
3988
|
-
JOIN pg_class t ON t.oid = conrelid
|
|
3989
|
-
JOIN pg_catalog.pg_namespace s
|
|
3990
|
-
ON s.oid = t.relnamespace
|
|
3991
|
-
AND contype IN ('p', 'f', 'c')
|
|
3992
|
-
AND ${filterSchema("s.nspname")}
|
|
3993
|
-
ORDER BY c.conname`;
|
|
3994
|
-
const triggersSql = `SELECT event_object_schema AS "schemaName",
|
|
3995
|
-
event_object_table AS "tableName",
|
|
3996
|
-
trigger_schema AS "triggerSchema",
|
|
3997
|
-
trigger_name AS name,
|
|
3998
|
-
json_agg(event_manipulation) AS events,
|
|
3999
|
-
action_timing AS activation,
|
|
4000
|
-
action_condition AS condition,
|
|
4001
|
-
action_statement AS definition
|
|
4002
|
-
FROM information_schema.triggers
|
|
4003
|
-
WHERE ${filterSchema("event_object_schema")}
|
|
4004
|
-
GROUP BY event_object_schema, event_object_table, trigger_schema, trigger_name, action_timing, action_condition, action_statement
|
|
4005
|
-
ORDER BY trigger_name`;
|
|
4006
|
-
const extensionsSql = `SELECT
|
|
4007
|
-
nspname AS "schemaName",
|
|
4008
|
-
extname AS "name",
|
|
4009
|
-
extversion AS version
|
|
4010
|
-
FROM pg_extension
|
|
4011
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = extnamespace
|
|
4012
|
-
AND ${filterSchema("n.nspname")}`;
|
|
4013
|
-
const enumsSql = `SELECT
|
|
4014
|
-
n.nspname as "schemaName",
|
|
4015
|
-
t.typname as name,
|
|
4016
|
-
json_agg(e.enumlabel ORDER BY e.enumsortorder) as values
|
|
4017
|
-
FROM pg_type t
|
|
4018
|
-
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
4019
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
4020
|
-
WHERE ${filterSchema("n.nspname")}
|
|
4021
|
-
GROUP BY n.nspname, t.typname`;
|
|
4022
|
-
const domainsSql = `SELECT
|
|
4023
|
-
n.nspname AS "schemaName",
|
|
4024
|
-
d.typname AS "name",
|
|
4025
|
-
t.typname AS "type",
|
|
4026
|
-
s.nspname AS "typeSchema",
|
|
4027
|
-
NOT d.typnotnull AS "isNullable",
|
|
4028
|
-
d.typndims AS "arrayDims",
|
|
4029
|
-
character_maximum_length AS "maxChars",
|
|
4030
|
-
numeric_precision AS "numericPrecision",
|
|
4031
|
-
numeric_scale AS "numericScale",
|
|
4032
|
-
datetime_precision AS "dateTimePrecision",
|
|
4033
|
-
collation_name AS "collate",
|
|
4034
|
-
domain_default AS "default",
|
|
4035
|
-
(
|
|
4036
|
-
SELECT json_agg(pg_get_expr(conbin, conrelid))
|
|
4037
|
-
FROM pg_catalog.pg_constraint c
|
|
4038
|
-
WHERE c.contypid = d.oid
|
|
4039
|
-
) AS "checks"
|
|
4040
|
-
FROM pg_catalog.pg_type d
|
|
4041
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = d.typnamespace
|
|
4042
|
-
JOIN information_schema.domains i
|
|
4043
|
-
ON i.domain_schema = nspname
|
|
4044
|
-
AND i.domain_name = d.typname
|
|
4045
|
-
JOIN pg_catalog.pg_type t
|
|
4046
|
-
ON (
|
|
4047
|
-
CASE WHEN d.typcategory = 'A'
|
|
4048
|
-
THEN t.typarray
|
|
4049
|
-
ELSE t.oid
|
|
4050
|
-
END
|
|
4051
|
-
) = d.typbasetype
|
|
4052
|
-
JOIN pg_catalog.pg_namespace s ON s.oid = t.typnamespace
|
|
4053
|
-
WHERE d.typtype = 'd' AND ${filterSchema("n.nspname")}`;
|
|
4054
|
-
const collationsSql = (version) => `SELECT
|
|
4055
|
-
nspname "schemaName",
|
|
4056
|
-
collname "name",
|
|
4057
|
-
CASE WHEN collprovider = 'i' THEN 'icu' WHEN collprovider = 'c' THEN 'libc' ELSE collprovider::text END "provider",
|
|
4058
|
-
collisdeterministic "deterministic",
|
|
4059
|
-
collcollate "lcCollate",
|
|
4060
|
-
collctype "lcCType",
|
|
4061
|
-
${version >= 17 ? "colllocale" : "colliculocale"} "locale",
|
|
4062
|
-
collversion "version"
|
|
4063
|
-
FROM pg_collation
|
|
4064
|
-
JOIN pg_namespace n on pg_collation.collnamespace = n.oid
|
|
4065
|
-
WHERE ${filterSchema("n.nspname")}`;
|
|
4066
|
-
const sql = (version) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
|
|
4067
|
-
tablesSql,
|
|
4068
|
-
"tables"
|
|
4069
|
-
)}, ${jsonAgg(viewsSql, "views")}, ${jsonAgg(
|
|
4070
|
-
indexesSql,
|
|
4071
|
-
"indexes"
|
|
4072
|
-
)}, ${jsonAgg(constraintsSql, "constraints")}, ${jsonAgg(
|
|
4073
|
-
triggersSql,
|
|
4074
|
-
"triggers"
|
|
4075
|
-
)}, ${jsonAgg(extensionsSql, "extensions")}, ${jsonAgg(
|
|
4076
|
-
enumsSql,
|
|
4077
|
-
"enums"
|
|
4078
|
-
)}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(
|
|
4079
|
-
collationsSql(version),
|
|
4080
|
-
"collations"
|
|
4081
|
-
)}`;
|
|
4082
|
-
async function introspectDbSchema(db) {
|
|
4083
|
-
const {
|
|
4084
|
-
rows: [{ version: versionString }]
|
|
4085
|
-
} = await db.query("SELECT version()");
|
|
4086
|
-
const version = +versionString.match(/\d+/)[0];
|
|
4087
|
-
const data = await db.query(sql(version));
|
|
4088
|
-
const result = data.rows[0];
|
|
4089
|
-
for (const domain of result.domains) {
|
|
4090
|
-
domain.checks = domain.checks?.filter((check) => check);
|
|
4091
|
-
nullsToUndefined(domain);
|
|
4092
|
-
}
|
|
4093
|
-
for (const table of result.tables) {
|
|
4094
|
-
for (const column of table.columns) {
|
|
4095
|
-
nullsToUndefined(column);
|
|
4096
|
-
if (column.identity) nullsToUndefined(column.identity);
|
|
4097
|
-
if (column.compression) {
|
|
4098
|
-
column.compression = column.compression === "p" ? "pglz" : "lz4";
|
|
4099
|
-
}
|
|
4100
|
-
}
|
|
4101
|
-
}
|
|
4102
|
-
const indexes = [];
|
|
4103
|
-
const excludes = [];
|
|
4104
|
-
for (const index of result.indexes) {
|
|
4105
|
-
nullsToUndefined(index);
|
|
4106
|
-
for (const column of index.columns) {
|
|
4107
|
-
if (!("expression" in column)) continue;
|
|
4108
|
-
const s = column.expression;
|
|
4109
|
-
const columnR = `"?\\w+"?`;
|
|
4110
|
-
const langR = `(${columnR}|'\\w+'::regconfig)`;
|
|
4111
|
-
const firstColumnR = `[(]*${columnR}`;
|
|
4112
|
-
const concatR = `\\|\\|`;
|
|
4113
|
-
const restColumnR = ` ${concatR} ' '::text\\) ${concatR} ${columnR}\\)`;
|
|
4114
|
-
const coalesceColumn = `COALESCE\\(${columnR}, ''::text\\)`;
|
|
4115
|
-
const tsVectorR = `to_tsvector\\(${langR}, (${firstColumnR}|${restColumnR}|${coalesceColumn})+\\)`;
|
|
4116
|
-
const weightR = `'\\w'::"char"`;
|
|
4117
|
-
const setWeightR = `setweight\\(${tsVectorR}, ${weightR}\\)`;
|
|
4118
|
-
const setWeightOrTsVectorR = `(${setWeightR}|${tsVectorR})`;
|
|
4119
|
-
const match = s.match(
|
|
4120
|
-
new RegExp(`^([\\(]*${setWeightOrTsVectorR}[\\)]*( ${concatR} )?)+$`)
|
|
4121
|
-
);
|
|
4122
|
-
if (!match) continue;
|
|
4123
|
-
let language;
|
|
4124
|
-
let languageColumn;
|
|
4125
|
-
const tokens = match[0].match(
|
|
4126
|
-
new RegExp(
|
|
4127
|
-
`setweight\\(|to_tsvector\\(${langR}|[:']?${columnR}\\(?`,
|
|
4128
|
-
"g"
|
|
4129
|
-
)
|
|
4130
|
-
)?.reduce((acc, token) => {
|
|
4131
|
-
if (token === "setweight(" || token === "COALESCE(" || token[0] === ":")
|
|
4132
|
-
return acc;
|
|
4133
|
-
if (token.startsWith("to_tsvector(")) {
|
|
4134
|
-
if (token[12] === "'") {
|
|
4135
|
-
language = token.slice(13, -12);
|
|
4136
|
-
} else {
|
|
4137
|
-
languageColumn = token.slice(12);
|
|
4138
|
-
}
|
|
4139
|
-
} else if (token[0] === "'") {
|
|
4140
|
-
acc.push({ kind: "weight", value: token[1] });
|
|
4141
|
-
} else {
|
|
4142
|
-
if (token[0] === '"') token = token.slice(1, -1);
|
|
4143
|
-
acc.push({ kind: "column", value: token });
|
|
4144
|
-
}
|
|
4145
|
-
return acc;
|
|
4146
|
-
}, []);
|
|
4147
|
-
if (!tokens) continue;
|
|
4148
|
-
index.language = language;
|
|
4149
|
-
index.languageColumn = languageColumn;
|
|
4150
|
-
index.tsVector = true;
|
|
4151
|
-
index.columns = [];
|
|
4152
|
-
for (const token of tokens) {
|
|
4153
|
-
if (token.kind === "column") {
|
|
4154
|
-
index.columns.push({
|
|
4155
|
-
column: token.value
|
|
4156
|
-
});
|
|
4157
|
-
} else if (token.kind === "weight") {
|
|
4158
|
-
index.columns[index.columns.length - 1].weight = token.value;
|
|
4159
|
-
}
|
|
4160
|
-
}
|
|
4161
|
-
}
|
|
4162
|
-
(index.exclude ? excludes : indexes).push(index);
|
|
4163
|
-
}
|
|
4164
|
-
result.indexes = indexes;
|
|
4165
|
-
result.excludes = excludes;
|
|
4166
|
-
return result;
|
|
4167
|
-
}
|
|
4168
|
-
const nullsToUndefined = (obj) => {
|
|
4169
|
-
for (const key in obj) {
|
|
4170
|
-
if (obj[key] === null)
|
|
4171
|
-
obj[key] = void 0;
|
|
4172
|
-
}
|
|
4173
|
-
};
|
|
4174
|
-
|
|
4175
|
-
const matchMap = {
|
|
4176
|
-
s: void 0,
|
|
4177
|
-
f: "FULL",
|
|
4178
|
-
p: "PARTIAL"
|
|
4179
|
-
};
|
|
4180
|
-
const fkeyActionMap = {
|
|
4181
|
-
a: void 0,
|
|
4182
|
-
// default
|
|
4183
|
-
r: "RESTRICT",
|
|
4184
|
-
c: "CASCADE",
|
|
4185
|
-
n: "SET NULL",
|
|
4186
|
-
d: "SET DEFAULT"
|
|
4187
|
-
};
|
|
4188
|
-
const makeStructureToAstCtx = (config, currentSchema) => ({
|
|
4189
|
-
snakeCase: config.snakeCase,
|
|
4190
|
-
unsupportedTypes: {},
|
|
4191
|
-
currentSchema,
|
|
4192
|
-
columnSchemaConfig: config.schemaConfig,
|
|
4193
|
-
columnsByType: makeColumnsByType(config.schemaConfig)
|
|
4194
|
-
});
|
|
4195
|
-
const structureToAst = async (ctx, adapter, config) => {
|
|
4196
|
-
const ast = [];
|
|
4197
|
-
const data = await introspectDbSchema(adapter);
|
|
4198
|
-
for (const name of data.schemas) {
|
|
4199
|
-
if (name === "public") continue;
|
|
4200
|
-
ast.push({
|
|
4201
|
-
type: "schema",
|
|
4202
|
-
action: "create",
|
|
4203
|
-
name
|
|
4204
|
-
});
|
|
4205
|
-
}
|
|
4206
|
-
for (const it of data.collations) {
|
|
4207
|
-
ast.push({
|
|
4208
|
-
type: "collation",
|
|
4209
|
-
action: "create",
|
|
4210
|
-
...it,
|
|
4211
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName
|
|
4212
|
-
});
|
|
4213
|
-
}
|
|
4214
|
-
const domains = makeDomainsMap(ctx, data);
|
|
4215
|
-
for (const table of data.tables) {
|
|
4216
|
-
if (table.name === config.migrationsTable) continue;
|
|
4217
|
-
ast.push(tableToAst(ctx, data, table, "create", domains));
|
|
4218
|
-
}
|
|
4219
|
-
for (const it of data.extensions) {
|
|
4220
|
-
ast.push({
|
|
4221
|
-
type: "extension",
|
|
4222
|
-
action: "create",
|
|
4223
|
-
name: it.name,
|
|
4224
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
4225
|
-
version: it.version
|
|
4226
|
-
});
|
|
4227
|
-
}
|
|
4228
|
-
for (const it of data.enums) {
|
|
4229
|
-
ast.push({
|
|
4230
|
-
type: "enum",
|
|
4231
|
-
action: "create",
|
|
4232
|
-
name: it.name,
|
|
4233
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
4234
|
-
values: it.values
|
|
4235
|
-
});
|
|
4236
|
-
}
|
|
4237
|
-
for (const it of data.domains) {
|
|
4238
|
-
ast.push({
|
|
4239
|
-
type: "domain",
|
|
4240
|
-
action: "create",
|
|
4241
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
4242
|
-
name: it.name,
|
|
4243
|
-
baseType: domains[`${it.schemaName}.${it.name}`]
|
|
4244
|
-
});
|
|
4245
|
-
}
|
|
4246
|
-
for (const table of data.tables) {
|
|
4247
|
-
for (const fkey of data.constraints) {
|
|
4248
|
-
if (fkey.references && fkey.tableName === table.name && fkey.schemaName === table.schemaName && checkIfIsOuterRecursiveFkey(data, table, fkey.references)) {
|
|
4249
|
-
ast.push({
|
|
4250
|
-
...constraintToAst(ctx, fkey),
|
|
4251
|
-
type: "constraint",
|
|
4252
|
-
action: "create",
|
|
4253
|
-
tableSchema: table.schemaName === ctx.currentSchema ? void 0 : table.schemaName,
|
|
4254
|
-
tableName: fkey.tableName
|
|
4255
|
-
});
|
|
4256
|
-
}
|
|
4257
|
-
}
|
|
4258
|
-
}
|
|
4259
|
-
for (const view of data.views) {
|
|
4260
|
-
ast.push(viewToAst(ctx, data, domains, view));
|
|
4261
|
-
}
|
|
4262
|
-
return ast;
|
|
4263
|
-
};
|
|
4264
|
-
const makeDomainsMap = (ctx, data) => {
|
|
4265
|
-
const domains = {};
|
|
4266
|
-
for (const it of data.domains) {
|
|
4267
|
-
const column = instantiateDbColumn(ctx, data, domains, {
|
|
4268
|
-
schemaName: it.schemaName,
|
|
4269
|
-
name: it.name,
|
|
4270
|
-
type: it.type,
|
|
4271
|
-
typeSchema: it.typeSchema,
|
|
4272
|
-
arrayDims: it.arrayDims,
|
|
4273
|
-
tableName: "",
|
|
4274
|
-
isNullable: it.isNullable,
|
|
4275
|
-
collate: it.collate,
|
|
4276
|
-
default: it.default,
|
|
4277
|
-
typmod: -1
|
|
4278
|
-
});
|
|
4279
|
-
if (it.checks) {
|
|
4280
|
-
column.data.checks = it.checks.map((check) => ({
|
|
4281
|
-
sql: new RawSql([[check]])
|
|
4282
|
-
}));
|
|
4283
|
-
}
|
|
4284
|
-
domains[`${it.schemaName}.${it.name}`] = column;
|
|
4285
|
-
}
|
|
4286
|
-
return domains;
|
|
4287
|
-
};
|
|
4288
|
-
const getDbColumnIsSerial = (item) => {
|
|
4289
|
-
if (item.type === "int2" || item.type === "int4" || item.type === "int8") {
|
|
4290
|
-
const { default: def, schemaName, tableName, name } = item;
|
|
4291
|
-
const seq = `${tableName}_${name}_seq`;
|
|
4292
|
-
if (def && (def === `nextval(${singleQuote(`${seq}`)}::regclass)` || def === `nextval(${singleQuote(`"${seq}"`)}::regclass)` || def === `nextval(${singleQuote(`${schemaName}.${seq}`)}::regclass)` || def === `nextval(${singleQuote(`"${schemaName}".${seq}`)}::regclass)` || def === `nextval(${singleQuote(`${schemaName}."${seq}"`)}::regclass)` || def === `nextval(${singleQuote(`"${schemaName}"."${seq}"`)}::regclass)`)) {
|
|
4293
|
-
return true;
|
|
4294
|
-
}
|
|
4295
|
-
}
|
|
4296
|
-
return false;
|
|
4297
|
-
};
|
|
4298
|
-
const instantiateDbColumn = (ctx, data, domains, dbColumn) => {
|
|
4299
|
-
var _a, _b;
|
|
4300
|
-
const isSerial = getDbColumnIsSerial(dbColumn);
|
|
4301
|
-
if (isSerial) {
|
|
4302
|
-
dbColumn = { ...dbColumn, default: void 0 };
|
|
4303
|
-
}
|
|
4304
|
-
let column;
|
|
4305
|
-
const col = instantiateColumnByDbType(ctx, dbColumn.type, isSerial, dbColumn);
|
|
4306
|
-
if (col) {
|
|
4307
|
-
column = col;
|
|
4308
|
-
} else {
|
|
4309
|
-
const { typeSchema, type: typeName } = dbColumn;
|
|
4310
|
-
const typeId = typeSchema === "pg_catalog" ? typeName : `${typeSchema}.${typeName}`;
|
|
4311
|
-
const domainColumn = domains[typeId];
|
|
4312
|
-
if (domainColumn) {
|
|
4313
|
-
column = new DomainColumn(
|
|
4314
|
-
ctx.columnSchemaConfig,
|
|
4315
|
-
typeName,
|
|
4316
|
-
typeSchema,
|
|
4317
|
-
dbColumn.extension
|
|
4318
|
-
).as(domainColumn);
|
|
4319
|
-
} else {
|
|
4320
|
-
const enumType = data.enums.find(
|
|
4321
|
-
(x) => x.name === typeName && x.schemaName === typeSchema
|
|
4322
|
-
);
|
|
4323
|
-
if (enumType) {
|
|
4324
|
-
column = new EnumColumn(
|
|
4325
|
-
ctx.columnSchemaConfig,
|
|
4326
|
-
typeSchema === ctx.currentSchema ? typeName : typeId,
|
|
4327
|
-
enumType.values,
|
|
4328
|
-
ctx.columnSchemaConfig.type
|
|
4329
|
-
);
|
|
4330
|
-
} else {
|
|
4331
|
-
column = new CustomTypeColumn(
|
|
4332
|
-
ctx.columnSchemaConfig,
|
|
4333
|
-
typeName,
|
|
4334
|
-
typeSchema === "pg_catalog" ? void 0 : typeSchema,
|
|
4335
|
-
dbColumn.extension
|
|
4336
|
-
);
|
|
4337
|
-
((_a = ctx.unsupportedTypes)[_b = dbColumn.type] ?? (_a[_b] = [])).push(
|
|
4338
|
-
`${dbColumn.schemaName}${dbColumn.tableName ? `.${dbColumn.tableName}` : ""}.${dbColumn.name}`
|
|
4339
|
-
);
|
|
4340
|
-
}
|
|
4341
|
-
assignDbDataToColumn(column, dbColumn);
|
|
4342
|
-
}
|
|
4343
|
-
}
|
|
4344
|
-
column.data.name = void 0;
|
|
4345
|
-
if (!column.data.isNullable) column.data.isNullable = void 0;
|
|
4346
|
-
if (dbColumn.arrayDims) {
|
|
4347
|
-
const arr = new ArrayColumn(
|
|
4348
|
-
ctx.columnSchemaConfig,
|
|
4349
|
-
column,
|
|
4350
|
-
ctx.columnSchemaConfig.type
|
|
4351
|
-
);
|
|
4352
|
-
arr.data.isNullable = dbColumn.isNullable;
|
|
4353
|
-
arr.data.arrayDims = dbColumn.arrayDims;
|
|
4354
|
-
column = arr;
|
|
4355
|
-
}
|
|
4356
|
-
return column;
|
|
4357
|
-
};
|
|
4358
|
-
const instantiateColumnByDbType = (ctx, type, isSerial, params) => {
|
|
4359
|
-
let columnFn = ctx.columnsByType[!isSerial ? type : type === "int2" ? "smallserial" : type === "int4" ? "serial" : "bigserial"];
|
|
4360
|
-
if (!columnFn && params.extension === "postgis" && type === "geography" && PostgisGeographyPointColumn.isDefaultPoint(params.typmod)) {
|
|
4361
|
-
columnFn = ctx.columnsByType.geographyDefaultPoint;
|
|
4362
|
-
}
|
|
4363
|
-
return columnFn ? assignDbDataToColumn(columnFn(), params) : void 0;
|
|
4364
|
-
};
|
|
4365
|
-
const tableToAst = (ctx, data, table, action, domains) => {
|
|
4366
|
-
const { schemaName, name: tableName } = table;
|
|
4367
|
-
const tableData = getDbStructureTableData(data, table);
|
|
4368
|
-
const { primaryKey, constraints } = tableData;
|
|
4369
|
-
return {
|
|
4370
|
-
type: "table",
|
|
4371
|
-
action,
|
|
4372
|
-
schema: schemaName === ctx.currentSchema ? void 0 : schemaName,
|
|
4373
|
-
comment: table.comment,
|
|
4374
|
-
name: tableName,
|
|
4375
|
-
shape: makeDbStructureColumnsShape(ctx, data, domains, table, tableData),
|
|
4376
|
-
noPrimaryKey: tableData.primaryKey ? "error" : "ignore",
|
|
4377
|
-
primaryKey: primaryKey && primaryKey.columns.length > 1 ? { ...primaryKey, columns: primaryKey.columns.map(toCamelCase) } : void 0,
|
|
4378
|
-
indexes: indexesOrExcludesToAst(
|
|
4379
|
-
tableName,
|
|
4380
|
-
tableData,
|
|
4381
|
-
"indexes"
|
|
4382
|
-
),
|
|
4383
|
-
excludes: indexesOrExcludesToAst(
|
|
4384
|
-
tableName,
|
|
4385
|
-
tableData,
|
|
4386
|
-
"excludes"
|
|
4387
|
-
),
|
|
4388
|
-
constraints: constraints.reduce((acc, it) => {
|
|
4389
|
-
if (it.check && it.references || it.check && it.check.columns?.length !== 1 || it.references && it.references.columns.length !== 1 && !checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
4390
|
-
acc.push(dbConstraintToTableConstraint(ctx, table, it));
|
|
4391
|
-
}
|
|
4392
|
-
return acc;
|
|
4393
|
-
}, [])
|
|
4394
|
-
};
|
|
4395
|
-
};
|
|
4396
|
-
const indexesOrExcludesToAst = (tableName, tableData, key) => {
|
|
4397
|
-
return tableData[key].reduce((acc, item) => {
|
|
4398
|
-
if (item.columns.length > 1 || item.columns.some((it) => "expression" in it)) {
|
|
4399
|
-
const options = makeIndexOrExcludeOptions(tableName, item, key);
|
|
4400
|
-
acc.push({
|
|
4401
|
-
columns: item.columns.map((it, i) => ({
|
|
4402
|
-
with: "exclude" in item && item.exclude ? item.exclude[i] : void 0,
|
|
4403
|
-
..."expression" in it ? { expression: it.expression } : { column: toCamelCase(it.column) },
|
|
4404
|
-
collate: it.collate,
|
|
4405
|
-
opclass: it.opclass,
|
|
4406
|
-
order: it.order
|
|
4407
|
-
})),
|
|
4408
|
-
options: {
|
|
4409
|
-
...options,
|
|
4410
|
-
include: item.include?.map(toCamelCase)
|
|
4411
|
-
}
|
|
4412
|
-
});
|
|
4413
|
-
}
|
|
4414
|
-
return acc;
|
|
4415
|
-
}, []);
|
|
4416
|
-
};
|
|
4417
|
-
const getDbStructureTableData = (data, { name, schemaName }) => {
|
|
4418
|
-
const filterFn = filterByTableSchema(name, schemaName);
|
|
4419
|
-
const constraints = data.constraints.filter(filterFn);
|
|
4420
|
-
const primaryKey = constraints.find((c) => c.primaryKey);
|
|
4421
|
-
return {
|
|
4422
|
-
primaryKey: primaryKey?.primaryKey ? {
|
|
4423
|
-
columns: primaryKey.primaryKey,
|
|
4424
|
-
name: primaryKey.name === `${name}_pkey` ? void 0 : primaryKey.name
|
|
4425
|
-
} : void 0,
|
|
4426
|
-
indexes: data.indexes.filter(filterFn),
|
|
4427
|
-
excludes: data.excludes.filter(filterFn),
|
|
4428
|
-
constraints
|
|
4429
|
-
};
|
|
4430
|
-
};
|
|
4431
|
-
const filterByTableSchema = (tableName, schemaName) => (x) => x.tableName === tableName && x.schemaName === schemaName;
|
|
4432
|
-
const constraintToAst = (ctx, item) => {
|
|
4433
|
-
const result = {};
|
|
4434
|
-
const { references, check } = item;
|
|
4435
|
-
if (references) {
|
|
4436
|
-
const options = {};
|
|
4437
|
-
result.references = {
|
|
4438
|
-
columns: references.columns,
|
|
4439
|
-
fnOrTable: getReferencesTable(ctx, references),
|
|
4440
|
-
foreignColumns: references.foreignColumns,
|
|
4441
|
-
options
|
|
4442
|
-
};
|
|
4443
|
-
const match = matchMap[references.match];
|
|
4444
|
-
if (match) options.match = match;
|
|
4445
|
-
const onUpdate = fkeyActionMap[references.onUpdate];
|
|
4446
|
-
if (onUpdate) options.onUpdate = onUpdate;
|
|
4447
|
-
const onDelete = fkeyActionMap[references.onDelete];
|
|
4448
|
-
if (onDelete) options.onDelete = onDelete;
|
|
4449
|
-
}
|
|
4450
|
-
if (check) {
|
|
4451
|
-
result.check = raw({ raw: check.expression });
|
|
4452
|
-
}
|
|
4453
|
-
if (item.name && item.name !== getConstraintName(item.tableName, result, ctx.snakeCase)) {
|
|
4454
|
-
result.name = item.name;
|
|
4455
|
-
if (result.references?.options) {
|
|
4456
|
-
result.references.options.name = item.name;
|
|
4457
|
-
}
|
|
4458
|
-
}
|
|
4459
|
-
return result;
|
|
4460
|
-
};
|
|
4461
|
-
const getReferencesTable = (ctx, references) => {
|
|
4462
|
-
return references.foreignSchema !== ctx.currentSchema ? `${references.foreignSchema}.${references.foreignTable}` : references.foreignTable;
|
|
4463
|
-
};
|
|
4464
|
-
const isColumnCheck = (it) => {
|
|
4465
|
-
return !it.references && it.check?.columns?.length === 1;
|
|
4466
|
-
};
|
|
4467
|
-
const viewToAst = (ctx, data, domains, view) => {
|
|
4468
|
-
const shape = makeDbStructureColumnsShape(ctx, data, domains, view);
|
|
4469
|
-
const options = {};
|
|
4470
|
-
if (view.isRecursive) options.recursive = true;
|
|
4471
|
-
if (view.with) {
|
|
4472
|
-
const withOptions = {};
|
|
4473
|
-
options.with = withOptions;
|
|
4474
|
-
for (const pair of view.with) {
|
|
4475
|
-
const [key, value] = pair.split("=");
|
|
4476
|
-
withOptions[toCamelCase(key)] = value === "true" ? true : value === "false" ? false : value;
|
|
4477
|
-
}
|
|
4478
|
-
}
|
|
4479
|
-
return {
|
|
4480
|
-
type: "view",
|
|
4481
|
-
action: "create",
|
|
4482
|
-
schema: view.schemaName === ctx.currentSchema ? void 0 : view.schemaName,
|
|
4483
|
-
name: view.name,
|
|
4484
|
-
shape,
|
|
4485
|
-
sql: raw({ raw: view.sql }),
|
|
4486
|
-
options,
|
|
4487
|
-
deps: view.deps
|
|
4488
|
-
};
|
|
4489
|
-
};
|
|
4490
|
-
const makeDbStructureColumnsShape = (ctx, data, domains, table, tableData) => {
|
|
4491
|
-
const shape = {};
|
|
4492
|
-
const checks = tableData ? getDbTableColumnsChecks(tableData) : void 0;
|
|
4493
|
-
for (const item of table.columns) {
|
|
4494
|
-
const [key, column] = dbColumnToAst(
|
|
4495
|
-
ctx,
|
|
4496
|
-
data,
|
|
4497
|
-
domains,
|
|
4498
|
-
table.name,
|
|
4499
|
-
item,
|
|
4500
|
-
table,
|
|
4501
|
-
tableData,
|
|
4502
|
-
checks
|
|
4503
|
-
);
|
|
4504
|
-
shape[key] = column;
|
|
4505
|
-
}
|
|
4506
|
-
return shape;
|
|
4507
|
-
};
|
|
4508
|
-
const getDbTableColumnsChecks = (tableData) => tableData.constraints.reduce((acc, item) => {
|
|
4509
|
-
var _a;
|
|
4510
|
-
if (isColumnCheck(item)) {
|
|
4511
|
-
(acc[_a = item.check.columns[0]] ?? (acc[_a] = [])).push(item.check.expression);
|
|
4512
|
-
}
|
|
4513
|
-
return acc;
|
|
4514
|
-
}, {});
|
|
4515
|
-
const dbColumnToAst = (ctx, data, domains, tableName, item, table, tableData, checks) => {
|
|
4516
|
-
let column = instantiateDbColumn(ctx, data, domains, item);
|
|
4517
|
-
column.data.name = item.name;
|
|
4518
|
-
if (item.identity) {
|
|
4519
|
-
column.data.identity = item.identity;
|
|
4520
|
-
if (!item.identity.always) delete column.data.identity?.always;
|
|
4521
|
-
}
|
|
4522
|
-
if (tableData?.primaryKey?.columns?.length === 1 && tableData?.primaryKey?.columns[0] === item.name) {
|
|
4523
|
-
column = column.primaryKey();
|
|
4524
|
-
}
|
|
4525
|
-
collectColumnIndexesOrExcludes(item, column, tableName, tableData, "indexes");
|
|
4526
|
-
collectColumnIndexesOrExcludes(
|
|
4527
|
-
item,
|
|
4528
|
-
column,
|
|
4529
|
-
tableName,
|
|
4530
|
-
tableData,
|
|
4531
|
-
"excludes"
|
|
4532
|
-
);
|
|
4533
|
-
if (table) {
|
|
4534
|
-
for (const it of data.constraints) {
|
|
4535
|
-
if (it.tableName !== table.name || it.schemaName !== table.schemaName || it.check || it.references?.columns.length !== 1 || it.references.columns[0] !== item.name || checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
4536
|
-
continue;
|
|
4537
|
-
}
|
|
4538
|
-
const c = dbConstraintToTableConstraint(ctx, table, it);
|
|
4539
|
-
column = column.foreignKey(
|
|
4540
|
-
c.references?.fnOrTable,
|
|
4541
|
-
it.references.foreignColumns[0],
|
|
4542
|
-
c.references?.options
|
|
4543
|
-
);
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
const columnChecks = checks?.[item.name];
|
|
4547
|
-
if (columnChecks) {
|
|
4548
|
-
column.data.checks = columnChecks.map((check) => ({
|
|
4549
|
-
sql: new RawSql([[check]])
|
|
4550
|
-
}));
|
|
4551
|
-
}
|
|
4552
|
-
const camelCaseName = toCamelCase(item.name);
|
|
4553
|
-
if (ctx.snakeCase) {
|
|
4554
|
-
const snakeCaseName = toSnakeCase(camelCaseName);
|
|
4555
|
-
if (snakeCaseName !== item.name) column.data.name = item.name;
|
|
4556
|
-
} else if (camelCaseName !== item.name) {
|
|
4557
|
-
column.data.name = item.name;
|
|
4558
|
-
}
|
|
4559
|
-
return [camelCaseName, column];
|
|
4560
|
-
};
|
|
4561
|
-
const collectColumnIndexesOrExcludes = (dbColumn, column, tableName, tableData, key) => {
|
|
4562
|
-
var _a;
|
|
4563
|
-
const items = tableData?.[key];
|
|
4564
|
-
if (!items) return;
|
|
4565
|
-
const columnItems = items.filter(
|
|
4566
|
-
(it) => it.columns.length === 1 && "column" in it.columns[0] && it.columns[0].column === dbColumn.name
|
|
4567
|
-
);
|
|
4568
|
-
for (const item of columnItems) {
|
|
4569
|
-
const columnOptions = item.columns[0];
|
|
4570
|
-
const { name, ...itemOptions } = makeIndexOrExcludeOptions(
|
|
4571
|
-
tableName,
|
|
4572
|
-
item,
|
|
4573
|
-
key
|
|
4574
|
-
);
|
|
4575
|
-
((_a = column.data)[key] ?? (_a[key] = [])).push({
|
|
4576
|
-
with: "exclude" in item && item.exclude ? item.exclude[0] : void 0,
|
|
4577
|
-
options: {
|
|
4578
|
-
name,
|
|
4579
|
-
collate: columnOptions.collate,
|
|
4580
|
-
opclass: columnOptions.opclass,
|
|
4581
|
-
order: columnOptions.order,
|
|
4582
|
-
...itemOptions
|
|
4583
|
-
}
|
|
4584
|
-
});
|
|
4585
|
-
}
|
|
4586
|
-
};
|
|
4587
|
-
const dbConstraintToTableConstraint = (ctx, table, item) => {
|
|
4588
|
-
const { references, check } = item;
|
|
4589
|
-
const constraint = {
|
|
4590
|
-
references: references ? {
|
|
4591
|
-
columns: references.columns,
|
|
4592
|
-
fnOrTable: getReferencesTable(ctx, references),
|
|
4593
|
-
foreignColumns: references.foreignColumns,
|
|
4594
|
-
options: {
|
|
4595
|
-
match: matchMap[references.match],
|
|
4596
|
-
onUpdate: fkeyActionMap[references.onUpdate],
|
|
4597
|
-
onDelete: fkeyActionMap[references.onDelete]
|
|
4598
|
-
}
|
|
4599
|
-
} : void 0,
|
|
4600
|
-
check: check ? raw({ raw: check.expression }) : void 0
|
|
4601
|
-
};
|
|
4602
|
-
const name = item.name && item.name !== getConstraintName(table.name, constraint, ctx.snakeCase) ? item.name : void 0;
|
|
4603
|
-
if (name) {
|
|
4604
|
-
constraint.name = name;
|
|
4605
|
-
if (constraint.references?.options) {
|
|
4606
|
-
constraint.references.options.name = name;
|
|
4607
|
-
}
|
|
4608
|
-
}
|
|
4609
|
-
return constraint;
|
|
4610
|
-
};
|
|
4611
|
-
const makeIndexOrExcludeOptions = (tableName, index, key) => {
|
|
4612
|
-
return {
|
|
4613
|
-
name: index.name !== (key === "indexes" ? getIndexName : getExcludeName)(
|
|
4614
|
-
tableName,
|
|
4615
|
-
index.columns
|
|
4616
|
-
) ? index.name : void 0,
|
|
4617
|
-
using: index.using === "btree" ? void 0 : index.using,
|
|
4618
|
-
unique: index.unique || void 0,
|
|
4619
|
-
include: index.include,
|
|
4620
|
-
nullsNotDistinct: index.nullsNotDistinct || void 0,
|
|
4621
|
-
with: index.with,
|
|
4622
|
-
tablespace: index.tablespace,
|
|
4623
|
-
where: index.where
|
|
4624
|
-
};
|
|
4625
|
-
};
|
|
4626
|
-
const checkIfIsOuterRecursiveFkey = (data, table, references) => {
|
|
4627
|
-
const referencesId = `${references.foreignSchema}.${references.foreignTable}`;
|
|
4628
|
-
const tableId = `${table.schemaName}.${table.name}`;
|
|
4629
|
-
for (const other of data.tables) {
|
|
4630
|
-
const id = `${other.schemaName}.${other.name}`;
|
|
4631
|
-
if (referencesId === id) {
|
|
4632
|
-
for (const c of data.constraints) {
|
|
4633
|
-
if (c.tableName === other.name && c.schemaName === other.schemaName && c.references?.foreignTable === table.name && c.references.foreignSchema === table.schemaName && tableId < id) {
|
|
4634
|
-
return true;
|
|
4635
|
-
}
|
|
4636
|
-
}
|
|
4637
|
-
break;
|
|
4638
|
-
}
|
|
4639
|
-
}
|
|
4640
|
-
return false;
|
|
4641
|
-
};
|
|
4642
|
-
|
|
4643
3707
|
const astToGenerateItems = (config, asts, currentSchema) => {
|
|
4644
3708
|
return asts.map((ast) => astToGenerateItem(config, ast, currentSchema));
|
|
4645
3709
|
};
|
|
@@ -5025,23 +4089,79 @@ const astEncoders = {
|
|
|
5025
4089
|
const inner = [`${quoteSchemaTable(ast)},`];
|
|
5026
4090
|
code.push(inner);
|
|
5027
4091
|
code = inner;
|
|
5028
|
-
if (hasOptions) {
|
|
5029
|
-
const options = [];
|
|
5030
|
-
if (ast.comment)
|
|
5031
|
-
options.push(`comment: ${JSON.stringify(ast.comment)},`);
|
|
5032
|
-
if (ast.noPrimaryKey === "ignore") options.push(`noPrimaryKey: true,`);
|
|
5033
|
-
code.push("{", options, "},");
|
|
5034
|
-
}
|
|
5035
|
-
code.push("(t) => ({");
|
|
4092
|
+
if (hasOptions) {
|
|
4093
|
+
const options = [];
|
|
4094
|
+
if (ast.comment)
|
|
4095
|
+
options.push(`comment: ${JSON.stringify(ast.comment)},`);
|
|
4096
|
+
if (ast.noPrimaryKey === "ignore") options.push(`noPrimaryKey: true,`);
|
|
4097
|
+
code.push("{", options, "},");
|
|
4098
|
+
}
|
|
4099
|
+
code.push("(t) => ({");
|
|
4100
|
+
} else {
|
|
4101
|
+
addCode(
|
|
4102
|
+
code,
|
|
4103
|
+
`await db.${ast.action}Table(${quoteSchemaTable(ast)}, (t) => ({`
|
|
4104
|
+
);
|
|
4105
|
+
}
|
|
4106
|
+
const timestamps = getHasTimestamps(
|
|
4107
|
+
ast.shape.createdAt,
|
|
4108
|
+
ast.shape.updatedAt
|
|
4109
|
+
);
|
|
4110
|
+
const toCodeCtx = {
|
|
4111
|
+
t: "t",
|
|
4112
|
+
table: ast.name,
|
|
4113
|
+
currentSchema,
|
|
4114
|
+
migration: true,
|
|
4115
|
+
snakeCase: config.snakeCase
|
|
4116
|
+
};
|
|
4117
|
+
for (const key in ast.shape) {
|
|
4118
|
+
if (timestamps.hasAnyTimestamps && (key === "createdAt" || key === "updatedAt"))
|
|
4119
|
+
continue;
|
|
4120
|
+
const line = [`${quoteObjectKey(key, config.snakeCase)}: `];
|
|
4121
|
+
const columnCode = ast.shape[key].toCode(toCodeCtx, key);
|
|
4122
|
+
for (const part of columnCode) {
|
|
4123
|
+
addCode(line, part);
|
|
4124
|
+
}
|
|
4125
|
+
addCode(line, ",");
|
|
4126
|
+
code.push(line);
|
|
4127
|
+
}
|
|
4128
|
+
if (timestamps.hasAnyTimestamps) {
|
|
4129
|
+
code.push([`...${timestampsToCode(timestamps)},`]);
|
|
4130
|
+
}
|
|
4131
|
+
if (isShifted) {
|
|
4132
|
+
addCode(code, "}),");
|
|
4133
|
+
if (hasTableData) pushTableDataCode(code, ast);
|
|
4134
|
+
addCode(result, ");");
|
|
4135
|
+
} else {
|
|
4136
|
+
addCode(result, "}));");
|
|
4137
|
+
}
|
|
4138
|
+
return result;
|
|
4139
|
+
},
|
|
4140
|
+
changeTable(ast, config, currentSchema) {
|
|
4141
|
+
let code = [];
|
|
4142
|
+
const result = code;
|
|
4143
|
+
const schemaTable = quoteSchemaTable({
|
|
4144
|
+
schema: ast.schema === currentSchema ? void 0 : ast.schema,
|
|
4145
|
+
name: ast.name
|
|
4146
|
+
});
|
|
4147
|
+
const { comment } = ast;
|
|
4148
|
+
if (comment !== void 0) {
|
|
4149
|
+
addCode(code, `await db.changeTable(`);
|
|
4150
|
+
const inner = [
|
|
4151
|
+
`${schemaTable},`,
|
|
4152
|
+
`{ comment: ${JSON.stringify(ast.comment)} },`,
|
|
4153
|
+
"(t) => ({"
|
|
4154
|
+
];
|
|
4155
|
+
code.push(inner);
|
|
4156
|
+
code = inner;
|
|
5036
4157
|
} else {
|
|
5037
|
-
addCode(
|
|
5038
|
-
code,
|
|
5039
|
-
`await db.${ast.action}Table(${quoteSchemaTable(ast)}, (t) => ({`
|
|
5040
|
-
);
|
|
4158
|
+
addCode(code, `await db.changeTable(${schemaTable}, (t) => ({`);
|
|
5041
4159
|
}
|
|
5042
|
-
const
|
|
5043
|
-
|
|
5044
|
-
|
|
4160
|
+
const [addTimestamps, dropTimestamps] = ["add", "drop"].map(
|
|
4161
|
+
(type) => getHasTimestamps(
|
|
4162
|
+
ast.shape.createdAt && "type" in ast.shape.createdAt && ast.shape.createdAt?.type === type ? ast.shape.createdAt.item : void 0,
|
|
4163
|
+
ast.shape.updatedAt && "type" in ast.shape.updatedAt && ast.shape.updatedAt?.type === type ? ast.shape.updatedAt.item : void 0
|
|
4164
|
+
)
|
|
5045
4165
|
);
|
|
5046
4166
|
const toCodeCtx = {
|
|
5047
4167
|
t: "t",
|
|
@@ -5051,349 +4171,1251 @@ const astEncoders = {
|
|
|
5051
4171
|
snakeCase: config.snakeCase
|
|
5052
4172
|
};
|
|
5053
4173
|
for (const key in ast.shape) {
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
4174
|
+
const changes = toArray(ast.shape[key]);
|
|
4175
|
+
for (const change of changes) {
|
|
4176
|
+
if (change.type === "add" || change.type === "drop") {
|
|
4177
|
+
if ((addTimestamps.hasAnyTimestamps || dropTimestamps.hasAnyTimestamps) && (key === "createdAt" || key === "updatedAt"))
|
|
4178
|
+
continue;
|
|
4179
|
+
const recreate = changes.length > 1;
|
|
4180
|
+
const line = [
|
|
4181
|
+
recreate ? `...t.${change.type}(t.name(${singleQuote(
|
|
4182
|
+
change.item.data.name ?? key
|
|
4183
|
+
)})` : `${quoteObjectKey(key, config.snakeCase)}: t.${change.type}(`
|
|
4184
|
+
];
|
|
4185
|
+
const columnCode = change.item.toCode(toCodeCtx, key);
|
|
4186
|
+
for (let i = 0; i < columnCode.length; i++) {
|
|
4187
|
+
let part = columnCode[i];
|
|
4188
|
+
if (recreate && !i) part = part.slice(1);
|
|
4189
|
+
addCode(line, part);
|
|
4190
|
+
}
|
|
4191
|
+
addCode(line, "),");
|
|
4192
|
+
code.push(line);
|
|
4193
|
+
} else if (change.type === "change") {
|
|
4194
|
+
if (!change.from.column || !change.to.column) continue;
|
|
4195
|
+
const line = [
|
|
4196
|
+
`${quoteObjectKey(key, config.snakeCase)}: t${change.name ? `.name(${singleQuote(change.name)})` : ""}.change(`
|
|
4197
|
+
];
|
|
4198
|
+
const fromCode = change.from.column.toCode(
|
|
4199
|
+
{
|
|
4200
|
+
t: "t",
|
|
4201
|
+
table: ast.name,
|
|
4202
|
+
currentSchema,
|
|
4203
|
+
migration: true,
|
|
4204
|
+
snakeCase: config.snakeCase
|
|
4205
|
+
},
|
|
4206
|
+
key
|
|
4207
|
+
);
|
|
4208
|
+
for (const part of fromCode) {
|
|
4209
|
+
addCode(line, part);
|
|
4210
|
+
}
|
|
4211
|
+
addCode(line, ", ");
|
|
4212
|
+
const toCode = change.to.column.toCode(toCodeCtx, key);
|
|
4213
|
+
for (const part of toCode) {
|
|
4214
|
+
addCode(line, part);
|
|
4215
|
+
}
|
|
4216
|
+
if (change.using) {
|
|
4217
|
+
addCode(line, ", {");
|
|
4218
|
+
const u = [];
|
|
4219
|
+
if (change.using.usingUp) {
|
|
4220
|
+
u.push(`usingUp: ${rawSqlToCode(change.using.usingUp, "t")},`);
|
|
4221
|
+
}
|
|
4222
|
+
if (change.using.usingDown) {
|
|
4223
|
+
u.push(
|
|
4224
|
+
`usingDown: ${rawSqlToCode(change.using.usingDown, "t")},`
|
|
4225
|
+
);
|
|
4226
|
+
}
|
|
4227
|
+
addCode(line, u);
|
|
4228
|
+
addCode(line, "}");
|
|
4229
|
+
}
|
|
4230
|
+
addCode(line, "),");
|
|
4231
|
+
code.push(line);
|
|
4232
|
+
} else if (change.type === "rename") {
|
|
4233
|
+
code.push([
|
|
4234
|
+
`${quoteObjectKey(key, config.snakeCase)}: t.rename(${singleQuote(
|
|
4235
|
+
change.name
|
|
4236
|
+
)}),`
|
|
4237
|
+
]);
|
|
4238
|
+
} else {
|
|
4239
|
+
exhaustive(change.type);
|
|
4240
|
+
}
|
|
5060
4241
|
}
|
|
5061
|
-
addCode(line, ",");
|
|
5062
|
-
code.push(line);
|
|
5063
4242
|
}
|
|
5064
|
-
|
|
5065
|
-
|
|
4243
|
+
for (const key of ["drop", "add"]) {
|
|
4244
|
+
const timestamps = key === "add" ? addTimestamps : dropTimestamps;
|
|
4245
|
+
if (timestamps.hasAnyTimestamps) {
|
|
4246
|
+
addCode(code, [`...t.${key}(${timestampsToCode(timestamps)}),`]);
|
|
4247
|
+
}
|
|
4248
|
+
const { primaryKey, indexes, excludes, constraints } = ast[key];
|
|
4249
|
+
if (primaryKey) {
|
|
4250
|
+
addCode(code, [
|
|
4251
|
+
`...t.${key}(${primaryKeyInnerToCode(primaryKey, "t")}),`
|
|
4252
|
+
]);
|
|
4253
|
+
}
|
|
4254
|
+
if (indexes) {
|
|
4255
|
+
for (const item of indexes) {
|
|
4256
|
+
addCode(code, [`...t.${key}(`, indexInnerToCode(item, "t"), "),"]);
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
if (excludes) {
|
|
4260
|
+
for (const item of excludes) {
|
|
4261
|
+
addCode(code, [`...t.${key}(`, excludeInnerToCode(item, "t"), "),"]);
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
if (constraints) {
|
|
4265
|
+
for (const item of constraints) {
|
|
4266
|
+
addCode(code, [
|
|
4267
|
+
`...t.${key}(`,
|
|
4268
|
+
constraintInnerToCode(item, "t", true),
|
|
4269
|
+
"),"
|
|
4270
|
+
]);
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
5066
4273
|
}
|
|
5067
|
-
if (
|
|
4274
|
+
if (ast.comment !== void 0) {
|
|
5068
4275
|
addCode(code, "}),");
|
|
5069
|
-
if (hasTableData) pushTableDataCode(code, ast);
|
|
5070
4276
|
addCode(result, ");");
|
|
5071
4277
|
} else {
|
|
5072
4278
|
addCode(result, "}));");
|
|
5073
4279
|
}
|
|
5074
|
-
return result;
|
|
5075
|
-
},
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
const
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
4280
|
+
return result;
|
|
4281
|
+
},
|
|
4282
|
+
renameType(ast, _, currentSchema) {
|
|
4283
|
+
const code = [];
|
|
4284
|
+
const kind = ast.kind === "TABLE" ? "Table" : "Type";
|
|
4285
|
+
if (ast.from === ast.to) {
|
|
4286
|
+
addCode(
|
|
4287
|
+
code,
|
|
4288
|
+
`await db.change${kind}Schema(${singleQuote(ast.to)}, ${singleQuote(
|
|
4289
|
+
ast.fromSchema ?? currentSchema
|
|
4290
|
+
)}, ${singleQuote(ast.toSchema ?? currentSchema)});`
|
|
4291
|
+
);
|
|
4292
|
+
} else {
|
|
4293
|
+
addCode(
|
|
4294
|
+
code,
|
|
4295
|
+
`await db.rename${kind}(${quoteSchemaTable({
|
|
4296
|
+
schema: ast.fromSchema === currentSchema ? void 0 : ast.fromSchema,
|
|
4297
|
+
name: ast.from
|
|
4298
|
+
})}, ${quoteSchemaTable({
|
|
4299
|
+
schema: ast.toSchema === currentSchema ? void 0 : ast.toSchema,
|
|
4300
|
+
name: ast.to
|
|
4301
|
+
})});`
|
|
4302
|
+
);
|
|
4303
|
+
}
|
|
4304
|
+
return code;
|
|
4305
|
+
},
|
|
4306
|
+
schema(ast) {
|
|
4307
|
+
return `await db.${ast.action === "create" ? "createSchema" : "dropSchema"}(${singleQuote(ast.name)});`;
|
|
4308
|
+
},
|
|
4309
|
+
renameSchema(ast) {
|
|
4310
|
+
return `await db.renameSchema(${singleQuote(ast.from)}, ${singleQuote(
|
|
4311
|
+
ast.to
|
|
4312
|
+
)});`;
|
|
4313
|
+
},
|
|
4314
|
+
extension(ast) {
|
|
4315
|
+
const code = [
|
|
4316
|
+
`await db.${ast.action}Extension(${quoteSchemaTable(ast)}`
|
|
4317
|
+
];
|
|
4318
|
+
if (ast.version) {
|
|
4319
|
+
addCode(code, ", {");
|
|
4320
|
+
code.push([`version: ${singleQuote(ast.version)},`], "}");
|
|
4321
|
+
}
|
|
4322
|
+
addCode(code, ");");
|
|
4323
|
+
return code;
|
|
4324
|
+
},
|
|
4325
|
+
enum(ast, _, currentSchema) {
|
|
4326
|
+
return `await db.${ast.action === "create" ? "createEnum" : "dropEnum"}(${quoteSchemaTable(ast, currentSchema)}, [${ast.values.map(singleQuote).join(", ")}]);`;
|
|
4327
|
+
},
|
|
4328
|
+
enumValues(ast, _, currentSchema) {
|
|
4329
|
+
return `await db.${ast.action}EnumValues(${quoteSchemaTable(
|
|
4330
|
+
ast,
|
|
4331
|
+
currentSchema
|
|
4332
|
+
)}, [${ast.values.map(singleQuote).join(", ")}]);`;
|
|
4333
|
+
},
|
|
4334
|
+
renameEnumValues(ast, config, currentSchema) {
|
|
4335
|
+
return `await db.renameEnumValues(${quoteSchemaTable(
|
|
4336
|
+
ast,
|
|
4337
|
+
currentSchema
|
|
4338
|
+
)}, { ${Object.entries(ast.values).map(
|
|
4339
|
+
([from, to]) => `${quoteObjectKey(from, config.snakeCase)}: ${singleQuote(to)}`
|
|
4340
|
+
).join(", ")} });`;
|
|
4341
|
+
},
|
|
4342
|
+
changeEnumValues(ast, _, currentSchema) {
|
|
4343
|
+
return `await db.changeEnumValues(${quoteSchemaTable(
|
|
4344
|
+
ast,
|
|
4345
|
+
currentSchema
|
|
4346
|
+
)}, [${ast.fromValues.map(singleQuote).join(", ")}], [${ast.toValues.map(singleQuote).join(", ")}]);`;
|
|
4347
|
+
},
|
|
4348
|
+
domain(ast, _, currentSchema) {
|
|
4349
|
+
return `await db.${ast.action}Domain(${quoteSchemaTable(
|
|
4350
|
+
ast
|
|
4351
|
+
)}, (t) => ${ast.baseType.toCode(
|
|
4352
|
+
{ t: "t", table: ast.name, currentSchema },
|
|
4353
|
+
ast.baseType.data.name ?? ""
|
|
4354
|
+
)});`;
|
|
4355
|
+
},
|
|
4356
|
+
collation(ast) {
|
|
4357
|
+
const params = [];
|
|
4358
|
+
if (ast.locale) params.push(`locale: '${ast.locale}',`);
|
|
4359
|
+
if (ast.lcCollate) params.push(`lcCollate: '${ast.lcCollate}',`);
|
|
4360
|
+
if (ast.lcCType) params.push(`lcCType: '${ast.lcCType}',`);
|
|
4361
|
+
if (ast.provider) params.push(`provider: '${ast.provider}',`);
|
|
4362
|
+
if (ast.deterministic) params.push(`deterministic: ${ast.deterministic},`);
|
|
4363
|
+
if (ast.version) params.push(`version: '${ast.version}',`);
|
|
4364
|
+
return [
|
|
4365
|
+
`await db.createCollation(${quoteSchemaTable(ast)}, {`,
|
|
4366
|
+
params,
|
|
4367
|
+
"});"
|
|
4368
|
+
];
|
|
4369
|
+
},
|
|
4370
|
+
constraint(ast) {
|
|
4371
|
+
const table = quoteSchemaTable({
|
|
4372
|
+
schema: ast.tableSchema,
|
|
4373
|
+
name: ast.tableName
|
|
4374
|
+
});
|
|
4375
|
+
if (ast.references) {
|
|
4376
|
+
return [
|
|
4377
|
+
`await db.addForeignKey(`,
|
|
4378
|
+
[`${table},`, ...referencesArgsToCode(ast.references, ast.name, true)],
|
|
4379
|
+
");"
|
|
4380
|
+
];
|
|
4381
|
+
}
|
|
4382
|
+
const check = ast.check;
|
|
4383
|
+
return [
|
|
4384
|
+
`await db.addCheck(${table}, ${rawSqlToCode(check, "t")}${ast.name ? `, ${singleQuote(ast.name)}` : ""});`
|
|
4385
|
+
];
|
|
4386
|
+
},
|
|
4387
|
+
renameTableItem(ast) {
|
|
4388
|
+
return [
|
|
4389
|
+
`await db.rename${ast.kind === "INDEX" ? "Index" : "Constraint"}(${quoteSchemaTable({
|
|
4390
|
+
schema: ast.tableSchema,
|
|
4391
|
+
name: ast.tableName
|
|
4392
|
+
})}, ${singleQuote(ast.from)}, ${singleQuote(ast.to)});`
|
|
4393
|
+
];
|
|
4394
|
+
},
|
|
4395
|
+
view(ast) {
|
|
4396
|
+
const code = [`await db.createView(${quoteSchemaTable(ast)}`];
|
|
4397
|
+
const options = [];
|
|
4398
|
+
if (ast.options.recursive) options.push("recursive: true,");
|
|
4399
|
+
const w = ast.options.with;
|
|
4400
|
+
if (w?.checkOption) options.push(`checkOption: '${w.checkOption}',`);
|
|
4401
|
+
if (w?.securityBarrier)
|
|
4402
|
+
options.push(`securityBarrier: ${w.securityBarrier},`);
|
|
4403
|
+
if (w?.securityInvoker)
|
|
4404
|
+
options.push(`securityInvoker: ${w.securityInvoker},`);
|
|
4405
|
+
if (options.length) {
|
|
4406
|
+
addCode(code, ", {");
|
|
4407
|
+
code.push(options, "}");
|
|
4408
|
+
}
|
|
4409
|
+
addCode(code, ", ");
|
|
4410
|
+
if (!ast.sql._values) {
|
|
4411
|
+
const raw = ast.sql._sql;
|
|
4412
|
+
let sql;
|
|
4413
|
+
if (typeof raw === "string") {
|
|
4414
|
+
sql = raw;
|
|
4415
|
+
} else {
|
|
4416
|
+
sql = "";
|
|
4417
|
+
const parts = raw[0];
|
|
4418
|
+
const last = parts.length - 1;
|
|
4419
|
+
for (let i = 0; i < last; i++) {
|
|
4420
|
+
sql += parts[i] + `\${${raw[i + 1]}}`;
|
|
4421
|
+
}
|
|
4422
|
+
sql += parts[last];
|
|
4423
|
+
}
|
|
4424
|
+
addCode(code, backtickQuote(sql));
|
|
5093
4425
|
} else {
|
|
5094
|
-
addCode(code,
|
|
4426
|
+
addCode(code, rawSqlToCode(ast.sql, "db"));
|
|
5095
4427
|
}
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
4428
|
+
addCode(code, ");");
|
|
4429
|
+
return code;
|
|
4430
|
+
}
|
|
4431
|
+
};
|
|
4432
|
+
const isTimestamp = (column, type) => {
|
|
4433
|
+
if (!column) return false;
|
|
4434
|
+
const { default: def } = column.data;
|
|
4435
|
+
return Boolean(
|
|
4436
|
+
column instanceof type && !column.data.isNullable && def && typeof def === "object" && isRawSQL(def) && (typeof def._sql === "object" ? def._sql[0][0] : def._sql) === "now()"
|
|
4437
|
+
);
|
|
4438
|
+
};
|
|
4439
|
+
const getHasTimestamps = (createdAt, updatedAt) => {
|
|
4440
|
+
const timestamps = getTimestampsInfo(createdAt, updatedAt, TimestampTZColumn);
|
|
4441
|
+
const timestampsNoTZ = getTimestampsInfo(
|
|
4442
|
+
createdAt,
|
|
4443
|
+
updatedAt,
|
|
4444
|
+
TimestampColumn
|
|
4445
|
+
);
|
|
4446
|
+
return {
|
|
4447
|
+
hasTZTimestamps: timestamps,
|
|
4448
|
+
hasAnyTimestamps: timestamps || timestampsNoTZ
|
|
4449
|
+
};
|
|
4450
|
+
};
|
|
4451
|
+
const getTimestampsInfo = (createdAt, updatedAt, type) => {
|
|
4452
|
+
return isTimestamp(createdAt, type) && isTimestamp(updatedAt, type) && (!createdAt?.data.name || createdAt?.data.name === "created_at") && (!updatedAt?.data.name || updatedAt?.data.name === "updated_at");
|
|
4453
|
+
};
|
|
4454
|
+
const timestampsToCode = ({ hasTZTimestamps }) => {
|
|
4455
|
+
const key = hasTZTimestamps ? "timestamps" : "timestampsNoTZ";
|
|
4456
|
+
return `t.${key}()`;
|
|
4457
|
+
};
|
|
4458
|
+
|
|
4459
|
+
const filterSchema = (table) => `${table} !~ '^pg_' AND ${table} != 'information_schema'`;
|
|
4460
|
+
const jsonAgg = (sql2, as) => `(SELECT coalesce(json_agg(t.*), '[]') FROM (${sql2}) t) AS "${as}"`;
|
|
4461
|
+
const columnsSql = ({
|
|
4462
|
+
schema,
|
|
4463
|
+
table,
|
|
4464
|
+
join = "",
|
|
4465
|
+
where
|
|
4466
|
+
}) => `SELECT
|
|
4467
|
+
${schema}.nspname "schemaName",
|
|
4468
|
+
${table}.relname "tableName",
|
|
4469
|
+
a.attname "name",
|
|
4470
|
+
t.typname "type",
|
|
4471
|
+
tn.nspname "typeSchema",
|
|
4472
|
+
a.attndims "arrayDims",
|
|
4473
|
+
information_schema._pg_char_max_length(tt.id, tt.mod) "maxChars",
|
|
4474
|
+
information_schema._pg_numeric_precision(tt.id, tt.mod) "numericPrecision",
|
|
4475
|
+
information_schema._pg_numeric_scale(tt.id,tt.mod) "numericScale",
|
|
4476
|
+
information_schema._pg_datetime_precision(tt.id,tt.mod) "dateTimePrecision",
|
|
4477
|
+
CAST(
|
|
4478
|
+
CASE WHEN a.attgenerated = ''
|
|
4479
|
+
THEN pg_get_expr(ad.adbin, ad.adrelid)
|
|
4480
|
+
END AS information_schema.character_data
|
|
4481
|
+
) AS "default",
|
|
4482
|
+
NOT (a.attnotnull OR (t.typtype = 'd' AND t.typnotnull)) AS "isNullable",
|
|
4483
|
+
co.collname AS "collate",
|
|
4484
|
+
NULLIF(a.attcompression, '') AS compression,
|
|
4485
|
+
pgd.description AS "comment",
|
|
4486
|
+
(
|
|
4487
|
+
CASE WHEN a.attidentity IN ('a', 'd') THEN (
|
|
4488
|
+
json_build_object(
|
|
4489
|
+
'always',
|
|
4490
|
+
a.attidentity = 'a',
|
|
4491
|
+
'start',
|
|
4492
|
+
seq.seqstart,
|
|
4493
|
+
'increment',
|
|
4494
|
+
seq.seqincrement,
|
|
4495
|
+
'min',
|
|
4496
|
+
nullif(seq.seqmin, 1),
|
|
4497
|
+
'max',
|
|
4498
|
+
nullif(seq.seqmax, (
|
|
4499
|
+
CASE t.typname
|
|
4500
|
+
WHEN 'int2' THEN 32767
|
|
4501
|
+
WHEN 'int4' THEN 2147483647
|
|
4502
|
+
WHEN 'int8' THEN 9223372036854775807
|
|
4503
|
+
ELSE NULL
|
|
4504
|
+
END
|
|
4505
|
+
)),
|
|
4506
|
+
'cache',
|
|
4507
|
+
seq.seqcache,
|
|
4508
|
+
'cycle',
|
|
4509
|
+
seq.seqcycle
|
|
4510
|
+
)
|
|
4511
|
+
) END
|
|
4512
|
+
) "identity",
|
|
4513
|
+
ext.extname "extension",
|
|
4514
|
+
a.atttypmod "typmod"
|
|
4515
|
+
FROM pg_attribute a
|
|
4516
|
+
${join}
|
|
4517
|
+
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
|
4518
|
+
JOIN pg_type t
|
|
4519
|
+
ON t.oid = (
|
|
4520
|
+
CASE WHEN a.attndims = 0
|
|
4521
|
+
THEN a.atttypid
|
|
4522
|
+
ELSE (SELECT t.typelem FROM pg_type t WHERE t.oid = a.atttypid)
|
|
4523
|
+
END
|
|
4524
|
+
)
|
|
4525
|
+
JOIN LATERAL (
|
|
4526
|
+
SELECT
|
|
4527
|
+
CASE WHEN t.typtype = 'd' THEN t.typbasetype ELSE t.oid END id,
|
|
4528
|
+
CASE WHEN t.typtype = 'd' THEN t.typtypmod ELSE a.atttypmod END mod
|
|
4529
|
+
) tt ON true
|
|
4530
|
+
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
|
4531
|
+
LEFT JOIN (pg_collation co JOIN pg_namespace nco ON (co.collnamespace = nco.oid))
|
|
4532
|
+
ON a.attcollation = co.oid AND (nco.nspname, co.collname) <> ('pg_catalog', 'default')
|
|
4533
|
+
LEFT JOIN pg_catalog.pg_description pgd
|
|
4534
|
+
ON pgd.objoid = a.attrelid
|
|
4535
|
+
AND pgd.objsubid = a.attnum
|
|
4536
|
+
LEFT JOIN (pg_depend dep JOIN pg_sequence seq ON (dep.classid = 'pg_class'::regclass AND dep.objid = seq.seqrelid AND dep.deptype = 'i'))
|
|
4537
|
+
ON (dep.refclassid = 'pg_class'::regclass AND dep.refobjid = ${table}.oid AND dep.refobjsubid = a.attnum)
|
|
4538
|
+
LEFT JOIN pg_depend d ON d.objid = t.oid AND d.classid = 'pg_type'::regclass AND d.deptype = 'e'
|
|
4539
|
+
LEFT JOIN pg_extension ext ON ext.oid = d.refobjid
|
|
4540
|
+
WHERE a.attnum > 0
|
|
4541
|
+
AND NOT a.attisdropped
|
|
4542
|
+
AND ${where}
|
|
4543
|
+
ORDER BY a.attnum`;
|
|
4544
|
+
const schemasSql = `SELECT coalesce(json_agg(nspname ORDER BY nspname), '[]')
|
|
4545
|
+
FROM pg_catalog.pg_namespace n
|
|
4546
|
+
WHERE ${filterSchema("nspname")}`;
|
|
4547
|
+
const tablesSql = `SELECT
|
|
4548
|
+
nspname AS "schemaName",
|
|
4549
|
+
relname AS "name",
|
|
4550
|
+
obj_description(c.oid) AS comment,
|
|
4551
|
+
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
4552
|
+
schema: "n",
|
|
4553
|
+
table: "c",
|
|
4554
|
+
where: "a.attrelid = c.oid"
|
|
4555
|
+
})}) t) AS "columns"
|
|
4556
|
+
FROM pg_class c
|
|
4557
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = relnamespace
|
|
4558
|
+
WHERE (relkind = 'r' OR relkind = 'p')
|
|
4559
|
+
AND ${filterSchema("nspname")}
|
|
4560
|
+
ORDER BY relname`;
|
|
4561
|
+
const viewsSql = `SELECT
|
|
4562
|
+
nc.nspname AS "schemaName",
|
|
4563
|
+
c.relname AS "name",
|
|
4564
|
+
(
|
|
4565
|
+
SELECT COALESCE(json_agg(t.*), '[]')
|
|
4566
|
+
FROM (
|
|
4567
|
+
SELECT
|
|
4568
|
+
ns.nspname AS "schemaName",
|
|
4569
|
+
obj.relname AS "name"
|
|
4570
|
+
FROM pg_class obj
|
|
4571
|
+
JOIN pg_depend dep ON dep.refobjid = obj.oid
|
|
4572
|
+
JOIN pg_rewrite rew ON rew.oid = dep.objid
|
|
4573
|
+
JOIN pg_namespace ns ON ns.oid = obj.relnamespace
|
|
4574
|
+
WHERE rew.ev_class = c.oid AND obj.oid <> c.oid
|
|
4575
|
+
) t
|
|
4576
|
+
) "deps",
|
|
4577
|
+
right(substring(r.ev_action from ':hasRecursive \\w'), 1)::bool AS "isRecursive",
|
|
4578
|
+
array_to_json(c.reloptions) AS "with",
|
|
4579
|
+
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
4580
|
+
schema: "nc",
|
|
4581
|
+
table: "c",
|
|
4582
|
+
where: "a.attrelid = c.oid"
|
|
4583
|
+
})}) t) AS "columns",
|
|
4584
|
+
pg_get_viewdef(c.oid) AS "sql"
|
|
4585
|
+
FROM pg_namespace nc
|
|
4586
|
+
JOIN pg_class c
|
|
4587
|
+
ON nc.oid = c.relnamespace
|
|
4588
|
+
AND c.relkind = 'v'
|
|
4589
|
+
AND c.relpersistence != 't'
|
|
4590
|
+
JOIN pg_rewrite r ON r.ev_class = c.oid
|
|
4591
|
+
WHERE ${filterSchema("nc.nspname")}
|
|
4592
|
+
ORDER BY c.relname`;
|
|
4593
|
+
const indexesSql = `SELECT
|
|
4594
|
+
n.nspname "schemaName",
|
|
4595
|
+
t.relname "tableName",
|
|
4596
|
+
ic.relname "name",
|
|
4597
|
+
am.amname AS "using",
|
|
4598
|
+
i.indisunique "unique",
|
|
4599
|
+
(
|
|
4600
|
+
SELECT json_agg(
|
|
4601
|
+
(
|
|
4602
|
+
CASE WHEN t.e = 0
|
|
4603
|
+
THEN jsonb_build_object('expression', pg_get_indexdef(i.indexrelid, t.i::int4, false))
|
|
4604
|
+
ELSE jsonb_build_object('column', (
|
|
4605
|
+
(
|
|
4606
|
+
SELECT attname
|
|
4607
|
+
FROM pg_catalog.pg_attribute
|
|
4608
|
+
WHERE attrelid = i.indrelid
|
|
4609
|
+
AND attnum = t.e
|
|
4610
|
+
)
|
|
4611
|
+
))
|
|
4612
|
+
END
|
|
4613
|
+
) || (
|
|
4614
|
+
CASE WHEN i.indcollation[t.i - 1] = 0
|
|
4615
|
+
THEN '{}'::jsonb
|
|
4616
|
+
ELSE (
|
|
4617
|
+
SELECT (
|
|
4618
|
+
CASE WHEN collname = 'default'
|
|
4619
|
+
THEN '{}'::jsonb
|
|
4620
|
+
ELSE jsonb_build_object('collate', collname)
|
|
4621
|
+
END
|
|
4622
|
+
)
|
|
4623
|
+
FROM pg_catalog.pg_collation
|
|
4624
|
+
WHERE oid = i.indcollation[t.i - 1]
|
|
4625
|
+
)
|
|
4626
|
+
END
|
|
4627
|
+
) || (
|
|
4628
|
+
SELECT
|
|
4629
|
+
CASE WHEN opcdefault AND attoptions IS NULL
|
|
4630
|
+
THEN '{}'::jsonb
|
|
4631
|
+
ELSE jsonb_build_object(
|
|
4632
|
+
'opclass', opcname || COALESCE('(' || array_to_string(attoptions, ', ') || ')', '')
|
|
4633
|
+
)
|
|
4634
|
+
END
|
|
4635
|
+
FROM pg_opclass
|
|
4636
|
+
LEFT JOIN pg_attribute
|
|
4637
|
+
ON attrelid = i.indexrelid
|
|
4638
|
+
AND attnum = t.i
|
|
4639
|
+
WHERE oid = i.indclass[t.i - 1]
|
|
4640
|
+
) || (
|
|
4641
|
+
CASE WHEN i.indoption[t.i - 1] = 0
|
|
4642
|
+
THEN '{}'::jsonb
|
|
4643
|
+
ELSE jsonb_build_object(
|
|
4644
|
+
'order',
|
|
4645
|
+
CASE
|
|
4646
|
+
WHEN i.indoption[t.i - 1] = 1 THEN 'DESC NULLS LAST'
|
|
4647
|
+
WHEN i.indoption[t.i - 1] = 2 THEN 'ASC NULLS FIRST'
|
|
4648
|
+
WHEN i.indoption[t.i - 1] = 3 THEN 'DESC'
|
|
4649
|
+
ELSE NULL
|
|
4650
|
+
END
|
|
4651
|
+
)
|
|
4652
|
+
END
|
|
5100
4653
|
)
|
|
5101
|
-
)
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
4654
|
+
)
|
|
4655
|
+
FROM unnest(i.indkey[:indnkeyatts - 1]) WITH ORDINALITY AS t(e, i)
|
|
4656
|
+
) "columns",
|
|
4657
|
+
(
|
|
4658
|
+
SELECT json_agg(
|
|
4659
|
+
(
|
|
4660
|
+
SELECT attname
|
|
4661
|
+
FROM pg_catalog.pg_attribute
|
|
4662
|
+
WHERE attrelid = i.indrelid
|
|
4663
|
+
AND attnum = j.e
|
|
4664
|
+
)
|
|
4665
|
+
)
|
|
4666
|
+
FROM unnest(i.indkey[indnkeyatts:]) AS j(e)
|
|
4667
|
+
) AS "include",
|
|
4668
|
+
(to_jsonb(i.*)->'indnullsnotdistinct')::bool AS "nullsNotDistinct",
|
|
4669
|
+
NULLIF(pg_catalog.array_to_string(
|
|
4670
|
+
ic.reloptions || array(SELECT 'toast.' || x FROM pg_catalog.unnest(tc.reloptions) x),
|
|
4671
|
+
', '
|
|
4672
|
+
), '') AS "with",
|
|
4673
|
+
(
|
|
4674
|
+
SELECT tablespace
|
|
4675
|
+
FROM pg_indexes
|
|
4676
|
+
WHERE schemaname = n.nspname
|
|
4677
|
+
AND indexname = ic.relname
|
|
4678
|
+
) AS tablespace,
|
|
4679
|
+
pg_get_expr(i.indpred, i.indrelid) AS "where",
|
|
4680
|
+
(
|
|
4681
|
+
CASE i.indisexclusion WHEN true
|
|
4682
|
+
THEN (
|
|
4683
|
+
SELECT json_agg(o.oprname)
|
|
4684
|
+
FROM pg_catalog.pg_constraint c, LATERAL unnest(c.conexclop) op_oid
|
|
4685
|
+
JOIN pg_operator o ON o.oid = op_oid
|
|
4686
|
+
WHERE c.conindid = ic.oid
|
|
4687
|
+
)
|
|
4688
|
+
END
|
|
4689
|
+
) "exclude"
|
|
4690
|
+
FROM pg_index i
|
|
4691
|
+
JOIN pg_class t ON t.oid = i.indrelid
|
|
4692
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
4693
|
+
JOIN pg_class ic ON ic.oid = i.indexrelid
|
|
4694
|
+
JOIN pg_am am ON am.oid = ic.relam
|
|
4695
|
+
LEFT JOIN pg_catalog.pg_class tc ON (ic.reltoastrelid = tc.oid)
|
|
4696
|
+
WHERE ${filterSchema("n.nspname")}
|
|
4697
|
+
AND NOT i.indisprimary
|
|
4698
|
+
ORDER BY ic.relname`;
|
|
4699
|
+
const constraintsSql = `SELECT
|
|
4700
|
+
s.nspname AS "schemaName",
|
|
4701
|
+
t.relname AS "tableName",
|
|
4702
|
+
c.conname AS "name",
|
|
4703
|
+
(
|
|
4704
|
+
SELECT json_agg(ccu.column_name)
|
|
4705
|
+
FROM information_schema.constraint_column_usage ccu
|
|
4706
|
+
WHERE contype = 'p'
|
|
4707
|
+
AND ccu.constraint_name = c.conname
|
|
4708
|
+
AND ccu.table_schema = s.nspname
|
|
4709
|
+
) AS "primaryKey",
|
|
4710
|
+
(
|
|
4711
|
+
SELECT
|
|
4712
|
+
json_build_object(
|
|
4713
|
+
'foreignSchema',
|
|
4714
|
+
fs.nspname,
|
|
4715
|
+
'foreignTable',
|
|
4716
|
+
ft.relname,
|
|
4717
|
+
'columns',
|
|
4718
|
+
(
|
|
4719
|
+
SELECT json_agg(ccu.column_name)
|
|
4720
|
+
FROM information_schema.key_column_usage ccu
|
|
4721
|
+
WHERE ccu.constraint_name = c.conname
|
|
4722
|
+
AND ccu.table_schema = cs.nspname
|
|
4723
|
+
),
|
|
4724
|
+
'foreignColumns',
|
|
4725
|
+
(
|
|
4726
|
+
SELECT json_agg(ccu.column_name)
|
|
4727
|
+
FROM information_schema.constraint_column_usage ccu
|
|
4728
|
+
WHERE ccu.constraint_name = c.conname
|
|
4729
|
+
AND ccu.table_schema = cs.nspname
|
|
4730
|
+
),
|
|
4731
|
+
'match',
|
|
4732
|
+
c.confmatchtype,
|
|
4733
|
+
'onUpdate',
|
|
4734
|
+
c.confupdtype,
|
|
4735
|
+
'onDelete',
|
|
4736
|
+
c.confdeltype
|
|
4737
|
+
)
|
|
4738
|
+
FROM pg_class ft
|
|
4739
|
+
JOIN pg_catalog.pg_namespace fs ON fs.oid = ft.relnamespace
|
|
4740
|
+
JOIN pg_catalog.pg_namespace cs ON cs.oid = c.connamespace
|
|
4741
|
+
WHERE contype = 'f' AND ft.oid = confrelid
|
|
4742
|
+
) AS "references",
|
|
4743
|
+
(
|
|
4744
|
+
SELECT
|
|
4745
|
+
CASE conbin IS NULL
|
|
4746
|
+
WHEN false THEN
|
|
4747
|
+
json_build_object(
|
|
4748
|
+
'columns',
|
|
4749
|
+
json_agg(ccu.column_name),
|
|
4750
|
+
'expression',
|
|
4751
|
+
pg_get_expr(conbin, conrelid)
|
|
4752
|
+
)
|
|
4753
|
+
END
|
|
4754
|
+
FROM information_schema.constraint_column_usage ccu
|
|
4755
|
+
WHERE conbin IS NOT NULL
|
|
4756
|
+
AND ccu.constraint_name = c.conname
|
|
4757
|
+
AND ccu.table_schema = s.nspname
|
|
4758
|
+
) AS "check"
|
|
4759
|
+
FROM pg_catalog.pg_constraint c
|
|
4760
|
+
JOIN pg_class t ON t.oid = conrelid
|
|
4761
|
+
JOIN pg_catalog.pg_namespace s
|
|
4762
|
+
ON s.oid = t.relnamespace
|
|
4763
|
+
AND contype IN ('p', 'f', 'c')
|
|
4764
|
+
AND ${filterSchema("s.nspname")}
|
|
4765
|
+
ORDER BY c.conname`;
|
|
4766
|
+
const triggersSql = `SELECT event_object_schema AS "schemaName",
|
|
4767
|
+
event_object_table AS "tableName",
|
|
4768
|
+
trigger_schema AS "triggerSchema",
|
|
4769
|
+
trigger_name AS name,
|
|
4770
|
+
json_agg(event_manipulation) AS events,
|
|
4771
|
+
action_timing AS activation,
|
|
4772
|
+
action_condition AS condition,
|
|
4773
|
+
action_statement AS definition
|
|
4774
|
+
FROM information_schema.triggers
|
|
4775
|
+
WHERE ${filterSchema("event_object_schema")}
|
|
4776
|
+
GROUP BY event_object_schema, event_object_table, trigger_schema, trigger_name, action_timing, action_condition, action_statement
|
|
4777
|
+
ORDER BY trigger_name`;
|
|
4778
|
+
const extensionsSql = `SELECT
|
|
4779
|
+
nspname AS "schemaName",
|
|
4780
|
+
extname AS "name",
|
|
4781
|
+
extversion AS version
|
|
4782
|
+
FROM pg_extension
|
|
4783
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = extnamespace
|
|
4784
|
+
AND ${filterSchema("n.nspname")}`;
|
|
4785
|
+
const enumsSql = `SELECT
|
|
4786
|
+
n.nspname as "schemaName",
|
|
4787
|
+
t.typname as name,
|
|
4788
|
+
json_agg(e.enumlabel ORDER BY e.enumsortorder) as values
|
|
4789
|
+
FROM pg_type t
|
|
4790
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
4791
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
4792
|
+
WHERE ${filterSchema("n.nspname")}
|
|
4793
|
+
GROUP BY n.nspname, t.typname`;
|
|
4794
|
+
const domainsSql = `SELECT
|
|
4795
|
+
n.nspname AS "schemaName",
|
|
4796
|
+
d.typname AS "name",
|
|
4797
|
+
t.typname AS "type",
|
|
4798
|
+
s.nspname AS "typeSchema",
|
|
4799
|
+
NOT d.typnotnull AS "isNullable",
|
|
4800
|
+
d.typndims AS "arrayDims",
|
|
4801
|
+
character_maximum_length AS "maxChars",
|
|
4802
|
+
numeric_precision AS "numericPrecision",
|
|
4803
|
+
numeric_scale AS "numericScale",
|
|
4804
|
+
datetime_precision AS "dateTimePrecision",
|
|
4805
|
+
collation_name AS "collate",
|
|
4806
|
+
domain_default AS "default",
|
|
4807
|
+
(
|
|
4808
|
+
SELECT json_agg(pg_get_expr(conbin, conrelid))
|
|
4809
|
+
FROM pg_catalog.pg_constraint c
|
|
4810
|
+
WHERE c.contypid = d.oid
|
|
4811
|
+
) AS "checks"
|
|
4812
|
+
FROM pg_catalog.pg_type d
|
|
4813
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = d.typnamespace
|
|
4814
|
+
JOIN information_schema.domains i
|
|
4815
|
+
ON i.domain_schema = nspname
|
|
4816
|
+
AND i.domain_name = d.typname
|
|
4817
|
+
JOIN pg_catalog.pg_type t
|
|
4818
|
+
ON (
|
|
4819
|
+
CASE WHEN d.typcategory = 'A'
|
|
4820
|
+
THEN t.typarray
|
|
4821
|
+
ELSE t.oid
|
|
4822
|
+
END
|
|
4823
|
+
) = d.typbasetype
|
|
4824
|
+
JOIN pg_catalog.pg_namespace s ON s.oid = t.typnamespace
|
|
4825
|
+
WHERE d.typtype = 'd' AND ${filterSchema("n.nspname")}`;
|
|
4826
|
+
const collationsSql = (version) => `SELECT
|
|
4827
|
+
nspname "schemaName",
|
|
4828
|
+
collname "name",
|
|
4829
|
+
CASE WHEN collprovider = 'i' THEN 'icu' WHEN collprovider = 'c' THEN 'libc' ELSE collprovider::text END "provider",
|
|
4830
|
+
collisdeterministic "deterministic",
|
|
4831
|
+
collcollate "lcCollate",
|
|
4832
|
+
collctype "lcCType",
|
|
4833
|
+
${version >= 17 ? "colllocale" : "colliculocale"} "locale",
|
|
4834
|
+
collversion "version"
|
|
4835
|
+
FROM pg_collation
|
|
4836
|
+
JOIN pg_namespace n on pg_collation.collnamespace = n.oid
|
|
4837
|
+
WHERE ${filterSchema("n.nspname")}`;
|
|
4838
|
+
const sql = (version) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
|
|
4839
|
+
tablesSql,
|
|
4840
|
+
"tables"
|
|
4841
|
+
)}, ${jsonAgg(viewsSql, "views")}, ${jsonAgg(
|
|
4842
|
+
indexesSql,
|
|
4843
|
+
"indexes"
|
|
4844
|
+
)}, ${jsonAgg(constraintsSql, "constraints")}, ${jsonAgg(
|
|
4845
|
+
triggersSql,
|
|
4846
|
+
"triggers"
|
|
4847
|
+
)}, ${jsonAgg(extensionsSql, "extensions")}, ${jsonAgg(
|
|
4848
|
+
enumsSql,
|
|
4849
|
+
"enums"
|
|
4850
|
+
)}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(
|
|
4851
|
+
collationsSql(version),
|
|
4852
|
+
"collations"
|
|
4853
|
+
)}`;
|
|
4854
|
+
async function introspectDbSchema(db) {
|
|
4855
|
+
const {
|
|
4856
|
+
rows: [{ version: versionString }]
|
|
4857
|
+
} = await db.query("SELECT version()");
|
|
4858
|
+
const version = +versionString.match(/\d+/)[0];
|
|
4859
|
+
const data = await db.query(sql(version));
|
|
4860
|
+
const result = data.rows[0];
|
|
4861
|
+
for (const domain of result.domains) {
|
|
4862
|
+
domain.checks = domain.checks?.filter((check) => check);
|
|
4863
|
+
nullsToUndefined(domain);
|
|
4864
|
+
}
|
|
4865
|
+
for (const table of result.tables) {
|
|
4866
|
+
for (const column of table.columns) {
|
|
4867
|
+
nullsToUndefined(column);
|
|
4868
|
+
if (column.identity) nullsToUndefined(column.identity);
|
|
4869
|
+
if (column.compression) {
|
|
4870
|
+
column.compression = column.compression === "p" ? "pglz" : "lz4";
|
|
5177
4871
|
}
|
|
5178
4872
|
}
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
if (
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
}
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
4873
|
+
}
|
|
4874
|
+
const indexes = [];
|
|
4875
|
+
const excludes = [];
|
|
4876
|
+
for (const index of result.indexes) {
|
|
4877
|
+
nullsToUndefined(index);
|
|
4878
|
+
for (const column of index.columns) {
|
|
4879
|
+
if (!("expression" in column)) continue;
|
|
4880
|
+
const s = column.expression;
|
|
4881
|
+
const columnR = `"?\\w+"?`;
|
|
4882
|
+
const langR = `(${columnR}|'\\w+'::regconfig)`;
|
|
4883
|
+
const firstColumnR = `[(]*${columnR}`;
|
|
4884
|
+
const concatR = `\\|\\|`;
|
|
4885
|
+
const restColumnR = ` ${concatR} ' '::text\\) ${concatR} ${columnR}\\)`;
|
|
4886
|
+
const coalesceColumn = `COALESCE\\(${columnR}, ''::text\\)`;
|
|
4887
|
+
const tsVectorR = `to_tsvector\\(${langR}, (${firstColumnR}|${restColumnR}|${coalesceColumn})+\\)`;
|
|
4888
|
+
const weightR = `'\\w'::"char"`;
|
|
4889
|
+
const setWeightR = `setweight\\(${tsVectorR}, ${weightR}\\)`;
|
|
4890
|
+
const setWeightOrTsVectorR = `(${setWeightR}|${tsVectorR})`;
|
|
4891
|
+
const match = s.match(
|
|
4892
|
+
new RegExp(`^([\\(]*${setWeightOrTsVectorR}[\\)]*( ${concatR} )?)+$`)
|
|
4893
|
+
);
|
|
4894
|
+
if (!match) continue;
|
|
4895
|
+
let language;
|
|
4896
|
+
let languageColumn;
|
|
4897
|
+
const tokens = match[0].match(
|
|
4898
|
+
new RegExp(
|
|
4899
|
+
`setweight\\(|to_tsvector\\(${langR}|[:']?${columnR}\\(?`,
|
|
4900
|
+
"g"
|
|
4901
|
+
)
|
|
4902
|
+
)?.reduce((acc, token) => {
|
|
4903
|
+
if (token === "setweight(" || token === "COALESCE(" || token[0] === ":")
|
|
4904
|
+
return acc;
|
|
4905
|
+
if (token.startsWith("to_tsvector(")) {
|
|
4906
|
+
if (token[12] === "'") {
|
|
4907
|
+
language = token.slice(13, -12);
|
|
4908
|
+
} else {
|
|
4909
|
+
languageColumn = token.slice(12);
|
|
4910
|
+
}
|
|
4911
|
+
} else if (token[0] === "'") {
|
|
4912
|
+
acc.push({ kind: "weight", value: token[1] });
|
|
4913
|
+
} else {
|
|
4914
|
+
if (token[0] === '"') token = token.slice(1, -1);
|
|
4915
|
+
acc.push({ kind: "column", value: token });
|
|
5193
4916
|
}
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
4917
|
+
return acc;
|
|
4918
|
+
}, []);
|
|
4919
|
+
if (!tokens) continue;
|
|
4920
|
+
index.language = language;
|
|
4921
|
+
index.languageColumn = languageColumn;
|
|
4922
|
+
index.tsVector = true;
|
|
4923
|
+
index.columns = [];
|
|
4924
|
+
for (const token of tokens) {
|
|
4925
|
+
if (token.kind === "column") {
|
|
4926
|
+
index.columns.push({
|
|
4927
|
+
column: token.value
|
|
4928
|
+
});
|
|
4929
|
+
} else if (token.kind === "weight") {
|
|
4930
|
+
index.columns[index.columns.length - 1].weight = token.value;
|
|
5198
4931
|
}
|
|
5199
4932
|
}
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
4933
|
+
}
|
|
4934
|
+
(index.exclude ? excludes : indexes).push(index);
|
|
4935
|
+
}
|
|
4936
|
+
result.indexes = indexes;
|
|
4937
|
+
result.excludes = excludes;
|
|
4938
|
+
return result;
|
|
4939
|
+
}
|
|
4940
|
+
const nullsToUndefined = (obj) => {
|
|
4941
|
+
for (const key in obj) {
|
|
4942
|
+
if (obj[key] === null)
|
|
4943
|
+
obj[key] = void 0;
|
|
4944
|
+
}
|
|
4945
|
+
};
|
|
4946
|
+
|
|
4947
|
+
const matchMap = {
|
|
4948
|
+
s: void 0,
|
|
4949
|
+
f: "FULL",
|
|
4950
|
+
p: "PARTIAL"
|
|
4951
|
+
};
|
|
4952
|
+
const fkeyActionMap = {
|
|
4953
|
+
a: void 0,
|
|
4954
|
+
// default
|
|
4955
|
+
r: "RESTRICT",
|
|
4956
|
+
c: "CASCADE",
|
|
4957
|
+
n: "SET NULL",
|
|
4958
|
+
d: "SET DEFAULT"
|
|
4959
|
+
};
|
|
4960
|
+
const makeStructureToAstCtx = (config, currentSchema) => ({
|
|
4961
|
+
snakeCase: config.snakeCase,
|
|
4962
|
+
unsupportedTypes: {},
|
|
4963
|
+
currentSchema,
|
|
4964
|
+
columnSchemaConfig: config.schemaConfig,
|
|
4965
|
+
columnsByType: makeColumnsByType(config.schemaConfig)
|
|
4966
|
+
});
|
|
4967
|
+
const structureToAst = async (ctx, adapter, config) => {
|
|
4968
|
+
const ast = [];
|
|
4969
|
+
const data = await introspectDbSchema(adapter);
|
|
4970
|
+
for (const name of data.schemas) {
|
|
4971
|
+
if (name === "public") continue;
|
|
4972
|
+
ast.push({
|
|
4973
|
+
type: "schema",
|
|
4974
|
+
action: "create",
|
|
4975
|
+
name
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
for (const it of data.collations) {
|
|
4979
|
+
ast.push({
|
|
4980
|
+
type: "collation",
|
|
4981
|
+
action: "create",
|
|
4982
|
+
...it,
|
|
4983
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName
|
|
4984
|
+
});
|
|
4985
|
+
}
|
|
4986
|
+
const domains = makeDomainsMap(ctx, data);
|
|
4987
|
+
const { schema: migrationsSchema = "public", table: migrationsTable } = getMigrationsSchemaAndTable(config);
|
|
4988
|
+
for (const table of data.tables) {
|
|
4989
|
+
if (table.name === migrationsTable && table.schemaName === migrationsSchema)
|
|
4990
|
+
continue;
|
|
4991
|
+
ast.push(tableToAst(ctx, data, table, "create", domains));
|
|
4992
|
+
}
|
|
4993
|
+
for (const it of data.extensions) {
|
|
4994
|
+
ast.push({
|
|
4995
|
+
type: "extension",
|
|
4996
|
+
action: "create",
|
|
4997
|
+
name: it.name,
|
|
4998
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
4999
|
+
version: it.version
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
5002
|
+
for (const it of data.enums) {
|
|
5003
|
+
ast.push({
|
|
5004
|
+
type: "enum",
|
|
5005
|
+
action: "create",
|
|
5006
|
+
name: it.name,
|
|
5007
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
5008
|
+
values: it.values
|
|
5009
|
+
});
|
|
5010
|
+
}
|
|
5011
|
+
for (const it of data.domains) {
|
|
5012
|
+
ast.push({
|
|
5013
|
+
type: "domain",
|
|
5014
|
+
action: "create",
|
|
5015
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
5016
|
+
name: it.name,
|
|
5017
|
+
baseType: domains[`${it.schemaName}.${it.name}`]
|
|
5018
|
+
});
|
|
5019
|
+
}
|
|
5020
|
+
for (const table of data.tables) {
|
|
5021
|
+
for (const fkey of data.constraints) {
|
|
5022
|
+
if (fkey.references && fkey.tableName === table.name && fkey.schemaName === table.schemaName && checkIfIsOuterRecursiveFkey(data, table, fkey.references)) {
|
|
5023
|
+
ast.push({
|
|
5024
|
+
...constraintToAst(ctx, fkey),
|
|
5025
|
+
type: "constraint",
|
|
5026
|
+
action: "create",
|
|
5027
|
+
tableSchema: table.schemaName === ctx.currentSchema ? void 0 : table.schemaName,
|
|
5028
|
+
tableName: fkey.tableName
|
|
5029
|
+
});
|
|
5208
5030
|
}
|
|
5209
5031
|
}
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5032
|
+
}
|
|
5033
|
+
for (const view of data.views) {
|
|
5034
|
+
ast.push(viewToAst(ctx, data, domains, view));
|
|
5035
|
+
}
|
|
5036
|
+
return ast;
|
|
5037
|
+
};
|
|
5038
|
+
const makeDomainsMap = (ctx, data) => {
|
|
5039
|
+
const domains = {};
|
|
5040
|
+
for (const it of data.domains) {
|
|
5041
|
+
const column = instantiateDbColumn(ctx, data, domains, {
|
|
5042
|
+
schemaName: it.schemaName,
|
|
5043
|
+
name: it.name,
|
|
5044
|
+
type: it.type,
|
|
5045
|
+
typeSchema: it.typeSchema,
|
|
5046
|
+
arrayDims: it.arrayDims,
|
|
5047
|
+
tableName: "",
|
|
5048
|
+
isNullable: it.isNullable,
|
|
5049
|
+
collate: it.collate,
|
|
5050
|
+
default: it.default,
|
|
5051
|
+
typmod: -1
|
|
5052
|
+
});
|
|
5053
|
+
if (it.checks) {
|
|
5054
|
+
column.data.checks = it.checks.map((check) => ({
|
|
5055
|
+
sql: new RawSql([[check]])
|
|
5056
|
+
}));
|
|
5057
|
+
}
|
|
5058
|
+
domains[`${it.schemaName}.${it.name}`] = column;
|
|
5059
|
+
}
|
|
5060
|
+
return domains;
|
|
5061
|
+
};
|
|
5062
|
+
const getDbColumnIsSerial = (item) => {
|
|
5063
|
+
if (item.type === "int2" || item.type === "int4" || item.type === "int8") {
|
|
5064
|
+
const { default: def, schemaName, tableName, name } = item;
|
|
5065
|
+
const seq = `${tableName}_${name}_seq`;
|
|
5066
|
+
if (def && (def === `nextval(${singleQuote(`${seq}`)}::regclass)` || def === `nextval(${singleQuote(`"${seq}"`)}::regclass)` || def === `nextval(${singleQuote(`${schemaName}.${seq}`)}::regclass)` || def === `nextval(${singleQuote(`"${schemaName}".${seq}`)}::regclass)` || def === `nextval(${singleQuote(`${schemaName}."${seq}"`)}::regclass)` || def === `nextval(${singleQuote(`"${schemaName}"."${seq}"`)}::regclass)`)) {
|
|
5067
|
+
return true;
|
|
5215
5068
|
}
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5069
|
+
}
|
|
5070
|
+
return false;
|
|
5071
|
+
};
|
|
5072
|
+
const instantiateDbColumn = (ctx, data, domains, dbColumn) => {
|
|
5073
|
+
var _a, _b;
|
|
5074
|
+
const isSerial = getDbColumnIsSerial(dbColumn);
|
|
5075
|
+
if (isSerial) {
|
|
5076
|
+
dbColumn = { ...dbColumn, default: void 0 };
|
|
5077
|
+
}
|
|
5078
|
+
let column;
|
|
5079
|
+
const col = instantiateColumnByDbType(ctx, dbColumn.type, isSerial, dbColumn);
|
|
5080
|
+
if (col) {
|
|
5081
|
+
column = col;
|
|
5082
|
+
} else {
|
|
5083
|
+
const { typeSchema, type: typeName } = dbColumn;
|
|
5084
|
+
const typeId = typeSchema === "pg_catalog" ? typeName : `${typeSchema}.${typeName}`;
|
|
5085
|
+
const domainColumn = domains[typeId];
|
|
5086
|
+
if (domainColumn) {
|
|
5087
|
+
column = new DomainColumn(
|
|
5088
|
+
ctx.columnSchemaConfig,
|
|
5089
|
+
typeName,
|
|
5090
|
+
typeSchema,
|
|
5091
|
+
dbColumn.extension
|
|
5092
|
+
).as(domainColumn);
|
|
5228
5093
|
} else {
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
`await db.rename${kind}(${quoteSchemaTable({
|
|
5232
|
-
schema: ast.fromSchema === currentSchema ? void 0 : ast.fromSchema,
|
|
5233
|
-
name: ast.from
|
|
5234
|
-
})}, ${quoteSchemaTable({
|
|
5235
|
-
schema: ast.toSchema === currentSchema ? void 0 : ast.toSchema,
|
|
5236
|
-
name: ast.to
|
|
5237
|
-
})});`
|
|
5094
|
+
const enumType = data.enums.find(
|
|
5095
|
+
(x) => x.name === typeName && x.schemaName === typeSchema
|
|
5238
5096
|
);
|
|
5097
|
+
if (enumType) {
|
|
5098
|
+
column = new EnumColumn(
|
|
5099
|
+
ctx.columnSchemaConfig,
|
|
5100
|
+
typeSchema === ctx.currentSchema ? typeName : typeId,
|
|
5101
|
+
enumType.values,
|
|
5102
|
+
ctx.columnSchemaConfig.type
|
|
5103
|
+
);
|
|
5104
|
+
} else {
|
|
5105
|
+
column = new CustomTypeColumn(
|
|
5106
|
+
ctx.columnSchemaConfig,
|
|
5107
|
+
typeName,
|
|
5108
|
+
typeSchema === "pg_catalog" ? void 0 : typeSchema,
|
|
5109
|
+
dbColumn.extension
|
|
5110
|
+
);
|
|
5111
|
+
((_a = ctx.unsupportedTypes)[_b = dbColumn.type] ?? (_a[_b] = [])).push(
|
|
5112
|
+
`${dbColumn.schemaName}${dbColumn.tableName ? `.${dbColumn.tableName}` : ""}.${dbColumn.name}`
|
|
5113
|
+
);
|
|
5114
|
+
}
|
|
5115
|
+
assignDbDataToColumn(column, dbColumn);
|
|
5239
5116
|
}
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
)
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5117
|
+
}
|
|
5118
|
+
column.data.name = void 0;
|
|
5119
|
+
if (!column.data.isNullable) column.data.isNullable = void 0;
|
|
5120
|
+
if (dbColumn.arrayDims) {
|
|
5121
|
+
const arr = new ArrayColumn(
|
|
5122
|
+
ctx.columnSchemaConfig,
|
|
5123
|
+
column,
|
|
5124
|
+
ctx.columnSchemaConfig.type
|
|
5125
|
+
);
|
|
5126
|
+
arr.data.isNullable = dbColumn.isNullable;
|
|
5127
|
+
arr.data.arrayDims = dbColumn.arrayDims;
|
|
5128
|
+
column = arr;
|
|
5129
|
+
}
|
|
5130
|
+
return column;
|
|
5131
|
+
};
|
|
5132
|
+
const instantiateColumnByDbType = (ctx, type, isSerial, params) => {
|
|
5133
|
+
let columnFn = ctx.columnsByType[!isSerial ? type : type === "int2" ? "smallserial" : type === "int4" ? "serial" : "bigserial"];
|
|
5134
|
+
if (!columnFn && params.extension === "postgis" && type === "geography" && PostgisGeographyPointColumn.isDefaultPoint(params.typmod)) {
|
|
5135
|
+
columnFn = ctx.columnsByType.geographyDefaultPoint;
|
|
5136
|
+
}
|
|
5137
|
+
return columnFn ? assignDbDataToColumn(columnFn(), params) : void 0;
|
|
5138
|
+
};
|
|
5139
|
+
const tableToAst = (ctx, data, table, action, domains) => {
|
|
5140
|
+
const { schemaName, name: tableName } = table;
|
|
5141
|
+
const tableData = getDbStructureTableData(data, table);
|
|
5142
|
+
const { primaryKey, constraints } = tableData;
|
|
5143
|
+
return {
|
|
5144
|
+
type: "table",
|
|
5145
|
+
action,
|
|
5146
|
+
schema: schemaName === ctx.currentSchema ? void 0 : schemaName,
|
|
5147
|
+
comment: table.comment,
|
|
5148
|
+
name: tableName,
|
|
5149
|
+
shape: makeDbStructureColumnsShape(ctx, data, domains, table, tableData),
|
|
5150
|
+
noPrimaryKey: tableData.primaryKey ? "error" : "ignore",
|
|
5151
|
+
primaryKey: primaryKey && primaryKey.columns.length > 1 ? { ...primaryKey, columns: primaryKey.columns.map(toCamelCase) } : void 0,
|
|
5152
|
+
indexes: indexesOrExcludesToAst(
|
|
5153
|
+
tableName,
|
|
5154
|
+
tableData,
|
|
5155
|
+
"indexes"
|
|
5156
|
+
),
|
|
5157
|
+
excludes: indexesOrExcludesToAst(
|
|
5158
|
+
tableName,
|
|
5159
|
+
tableData,
|
|
5160
|
+
"excludes"
|
|
5161
|
+
),
|
|
5162
|
+
constraints: constraints.reduce((acc, it) => {
|
|
5163
|
+
if (it.check && it.references || it.check && it.check.columns?.length !== 1 || it.references && it.references.columns.length !== 1 && !checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
5164
|
+
acc.push(dbConstraintToTableConstraint(ctx, table, it));
|
|
5165
|
+
}
|
|
5166
|
+
return acc;
|
|
5167
|
+
}, [])
|
|
5168
|
+
};
|
|
5169
|
+
};
|
|
5170
|
+
const indexesOrExcludesToAst = (tableName, tableData, key) => {
|
|
5171
|
+
return tableData[key].reduce((acc, item) => {
|
|
5172
|
+
if (item.columns.length > 1 || item.columns.some((it) => "expression" in it)) {
|
|
5173
|
+
const options = makeIndexOrExcludeOptions(tableName, item, key);
|
|
5174
|
+
acc.push({
|
|
5175
|
+
columns: item.columns.map((it, i) => ({
|
|
5176
|
+
with: "exclude" in item && item.exclude ? item.exclude[i] : void 0,
|
|
5177
|
+
..."expression" in it ? { expression: it.expression } : { column: toCamelCase(it.column) },
|
|
5178
|
+
collate: it.collate,
|
|
5179
|
+
opclass: it.opclass,
|
|
5180
|
+
order: it.order
|
|
5181
|
+
})),
|
|
5182
|
+
options: {
|
|
5183
|
+
...options,
|
|
5184
|
+
include: item.include?.map(toCamelCase)
|
|
5185
|
+
}
|
|
5186
|
+
});
|
|
5257
5187
|
}
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
}
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
)
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
if (
|
|
5300
|
-
|
|
5301
|
-
`await db.createCollation(${quoteSchemaTable(ast)}, {`,
|
|
5302
|
-
params,
|
|
5303
|
-
"});"
|
|
5304
|
-
];
|
|
5305
|
-
},
|
|
5306
|
-
constraint(ast) {
|
|
5307
|
-
const table = quoteSchemaTable({
|
|
5308
|
-
schema: ast.tableSchema,
|
|
5309
|
-
name: ast.tableName
|
|
5310
|
-
});
|
|
5311
|
-
if (ast.references) {
|
|
5312
|
-
return [
|
|
5313
|
-
`await db.addForeignKey(`,
|
|
5314
|
-
[`${table},`, ...referencesArgsToCode(ast.references, ast.name, true)],
|
|
5315
|
-
");"
|
|
5316
|
-
];
|
|
5188
|
+
return acc;
|
|
5189
|
+
}, []);
|
|
5190
|
+
};
|
|
5191
|
+
const getDbStructureTableData = (data, { name, schemaName }) => {
|
|
5192
|
+
const filterFn = filterByTableSchema(name, schemaName);
|
|
5193
|
+
const constraints = data.constraints.filter(filterFn);
|
|
5194
|
+
const primaryKey = constraints.find((c) => c.primaryKey);
|
|
5195
|
+
return {
|
|
5196
|
+
primaryKey: primaryKey?.primaryKey ? {
|
|
5197
|
+
columns: primaryKey.primaryKey,
|
|
5198
|
+
name: primaryKey.name === `${name}_pkey` ? void 0 : primaryKey.name
|
|
5199
|
+
} : void 0,
|
|
5200
|
+
indexes: data.indexes.filter(filterFn),
|
|
5201
|
+
excludes: data.excludes.filter(filterFn),
|
|
5202
|
+
constraints
|
|
5203
|
+
};
|
|
5204
|
+
};
|
|
5205
|
+
const filterByTableSchema = (tableName, schemaName) => (x) => x.tableName === tableName && x.schemaName === schemaName;
|
|
5206
|
+
const constraintToAst = (ctx, item) => {
|
|
5207
|
+
const result = {};
|
|
5208
|
+
const { references, check } = item;
|
|
5209
|
+
if (references) {
|
|
5210
|
+
const options = {};
|
|
5211
|
+
result.references = {
|
|
5212
|
+
columns: references.columns,
|
|
5213
|
+
fnOrTable: getReferencesTable(ctx, references),
|
|
5214
|
+
foreignColumns: references.foreignColumns,
|
|
5215
|
+
options
|
|
5216
|
+
};
|
|
5217
|
+
const match = matchMap[references.match];
|
|
5218
|
+
if (match) options.match = match;
|
|
5219
|
+
const onUpdate = fkeyActionMap[references.onUpdate];
|
|
5220
|
+
if (onUpdate) options.onUpdate = onUpdate;
|
|
5221
|
+
const onDelete = fkeyActionMap[references.onDelete];
|
|
5222
|
+
if (onDelete) options.onDelete = onDelete;
|
|
5223
|
+
}
|
|
5224
|
+
if (check) {
|
|
5225
|
+
result.check = raw({ raw: check.expression });
|
|
5226
|
+
}
|
|
5227
|
+
if (item.name && item.name !== getConstraintName(item.tableName, result, ctx.snakeCase)) {
|
|
5228
|
+
result.name = item.name;
|
|
5229
|
+
if (result.references?.options) {
|
|
5230
|
+
result.references.options.name = item.name;
|
|
5317
5231
|
}
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
}
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
view
|
|
5332
|
-
const
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
if (w?.securityBarrier)
|
|
5338
|
-
options.push(`securityBarrier: ${w.securityBarrier},`);
|
|
5339
|
-
if (w?.securityInvoker)
|
|
5340
|
-
options.push(`securityInvoker: ${w.securityInvoker},`);
|
|
5341
|
-
if (options.length) {
|
|
5342
|
-
addCode(code, ", {");
|
|
5343
|
-
code.push(options, "}");
|
|
5232
|
+
}
|
|
5233
|
+
return result;
|
|
5234
|
+
};
|
|
5235
|
+
const getReferencesTable = (ctx, references) => {
|
|
5236
|
+
return references.foreignSchema !== ctx.currentSchema ? `${references.foreignSchema}.${references.foreignTable}` : references.foreignTable;
|
|
5237
|
+
};
|
|
5238
|
+
const isColumnCheck = (it) => {
|
|
5239
|
+
return !it.references && it.check?.columns?.length === 1;
|
|
5240
|
+
};
|
|
5241
|
+
const viewToAst = (ctx, data, domains, view) => {
|
|
5242
|
+
const shape = makeDbStructureColumnsShape(ctx, data, domains, view);
|
|
5243
|
+
const options = {};
|
|
5244
|
+
if (view.isRecursive) options.recursive = true;
|
|
5245
|
+
if (view.with) {
|
|
5246
|
+
const withOptions = {};
|
|
5247
|
+
options.with = withOptions;
|
|
5248
|
+
for (const pair of view.with) {
|
|
5249
|
+
const [key, value] = pair.split("=");
|
|
5250
|
+
withOptions[toCamelCase(key)] = value === "true" ? true : value === "false" ? false : value;
|
|
5344
5251
|
}
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5252
|
+
}
|
|
5253
|
+
return {
|
|
5254
|
+
type: "view",
|
|
5255
|
+
action: "create",
|
|
5256
|
+
schema: view.schemaName === ctx.currentSchema ? void 0 : view.schemaName,
|
|
5257
|
+
name: view.name,
|
|
5258
|
+
shape,
|
|
5259
|
+
sql: raw({ raw: view.sql }),
|
|
5260
|
+
options,
|
|
5261
|
+
deps: view.deps
|
|
5262
|
+
};
|
|
5263
|
+
};
|
|
5264
|
+
const makeDbStructureColumnsShape = (ctx, data, domains, table, tableData) => {
|
|
5265
|
+
const shape = {};
|
|
5266
|
+
const checks = tableData ? getDbTableColumnsChecks(tableData) : void 0;
|
|
5267
|
+
for (const item of table.columns) {
|
|
5268
|
+
const [key, column] = dbColumnToAst(
|
|
5269
|
+
ctx,
|
|
5270
|
+
data,
|
|
5271
|
+
domains,
|
|
5272
|
+
table.name,
|
|
5273
|
+
item,
|
|
5274
|
+
table,
|
|
5275
|
+
tableData,
|
|
5276
|
+
checks
|
|
5277
|
+
);
|
|
5278
|
+
shape[key] = column;
|
|
5279
|
+
}
|
|
5280
|
+
return shape;
|
|
5281
|
+
};
|
|
5282
|
+
const getDbTableColumnsChecks = (tableData) => tableData.constraints.reduce((acc, item) => {
|
|
5283
|
+
var _a;
|
|
5284
|
+
if (isColumnCheck(item)) {
|
|
5285
|
+
(acc[_a = item.check.columns[0]] ?? (acc[_a] = [])).push(item.check.expression);
|
|
5286
|
+
}
|
|
5287
|
+
return acc;
|
|
5288
|
+
}, {});
|
|
5289
|
+
const dbColumnToAst = (ctx, data, domains, tableName, item, table, tableData, checks) => {
|
|
5290
|
+
let column = instantiateDbColumn(ctx, data, domains, item);
|
|
5291
|
+
column.data.name = item.name;
|
|
5292
|
+
if (item.identity) {
|
|
5293
|
+
column.data.identity = item.identity;
|
|
5294
|
+
if (!item.identity.always) delete column.data.identity?.always;
|
|
5295
|
+
}
|
|
5296
|
+
if (tableData?.primaryKey?.columns?.length === 1 && tableData?.primaryKey?.columns[0] === item.name) {
|
|
5297
|
+
column = column.primaryKey();
|
|
5298
|
+
}
|
|
5299
|
+
collectColumnIndexesOrExcludes(item, column, tableName, tableData, "indexes");
|
|
5300
|
+
collectColumnIndexesOrExcludes(
|
|
5301
|
+
item,
|
|
5302
|
+
column,
|
|
5303
|
+
tableName,
|
|
5304
|
+
tableData,
|
|
5305
|
+
"excludes"
|
|
5306
|
+
);
|
|
5307
|
+
if (table) {
|
|
5308
|
+
for (const it of data.constraints) {
|
|
5309
|
+
if (it.tableName !== table.name || it.schemaName !== table.schemaName || it.check || it.references?.columns.length !== 1 || it.references.columns[0] !== item.name || checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
5310
|
+
continue;
|
|
5359
5311
|
}
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5312
|
+
const c = dbConstraintToTableConstraint(ctx, table, it);
|
|
5313
|
+
column = column.foreignKey(
|
|
5314
|
+
c.references?.fnOrTable,
|
|
5315
|
+
it.references.foreignColumns[0],
|
|
5316
|
+
c.references?.options
|
|
5317
|
+
);
|
|
5363
5318
|
}
|
|
5364
|
-
addCode(code, ");");
|
|
5365
|
-
return code;
|
|
5366
5319
|
}
|
|
5320
|
+
const columnChecks = checks?.[item.name];
|
|
5321
|
+
if (columnChecks) {
|
|
5322
|
+
column.data.checks = columnChecks.map((check) => ({
|
|
5323
|
+
sql: new RawSql([[check]])
|
|
5324
|
+
}));
|
|
5325
|
+
}
|
|
5326
|
+
const camelCaseName = toCamelCase(item.name);
|
|
5327
|
+
if (ctx.snakeCase) {
|
|
5328
|
+
const snakeCaseName = toSnakeCase(camelCaseName);
|
|
5329
|
+
if (snakeCaseName !== item.name) column.data.name = item.name;
|
|
5330
|
+
} else if (camelCaseName !== item.name) {
|
|
5331
|
+
column.data.name = item.name;
|
|
5332
|
+
}
|
|
5333
|
+
return [camelCaseName, column];
|
|
5367
5334
|
};
|
|
5368
|
-
const
|
|
5369
|
-
|
|
5370
|
-
const
|
|
5371
|
-
|
|
5372
|
-
|
|
5335
|
+
const collectColumnIndexesOrExcludes = (dbColumn, column, tableName, tableData, key) => {
|
|
5336
|
+
var _a;
|
|
5337
|
+
const items = tableData?.[key];
|
|
5338
|
+
if (!items) return;
|
|
5339
|
+
const columnItems = items.filter(
|
|
5340
|
+
(it) => it.columns.length === 1 && "column" in it.columns[0] && it.columns[0].column === dbColumn.name
|
|
5373
5341
|
);
|
|
5342
|
+
for (const item of columnItems) {
|
|
5343
|
+
const columnOptions = item.columns[0];
|
|
5344
|
+
const { name, ...itemOptions } = makeIndexOrExcludeOptions(
|
|
5345
|
+
tableName,
|
|
5346
|
+
item,
|
|
5347
|
+
key
|
|
5348
|
+
);
|
|
5349
|
+
((_a = column.data)[key] ?? (_a[key] = [])).push({
|
|
5350
|
+
with: "exclude" in item && item.exclude ? item.exclude[0] : void 0,
|
|
5351
|
+
options: {
|
|
5352
|
+
name,
|
|
5353
|
+
collate: columnOptions.collate,
|
|
5354
|
+
opclass: columnOptions.opclass,
|
|
5355
|
+
order: columnOptions.order,
|
|
5356
|
+
...itemOptions
|
|
5357
|
+
}
|
|
5358
|
+
});
|
|
5359
|
+
}
|
|
5374
5360
|
};
|
|
5375
|
-
const
|
|
5376
|
-
const
|
|
5377
|
-
const
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5361
|
+
const dbConstraintToTableConstraint = (ctx, table, item) => {
|
|
5362
|
+
const { references, check } = item;
|
|
5363
|
+
const constraint = {
|
|
5364
|
+
references: references ? {
|
|
5365
|
+
columns: references.columns,
|
|
5366
|
+
fnOrTable: getReferencesTable(ctx, references),
|
|
5367
|
+
foreignColumns: references.foreignColumns,
|
|
5368
|
+
options: {
|
|
5369
|
+
match: matchMap[references.match],
|
|
5370
|
+
onUpdate: fkeyActionMap[references.onUpdate],
|
|
5371
|
+
onDelete: fkeyActionMap[references.onDelete]
|
|
5372
|
+
}
|
|
5373
|
+
} : void 0,
|
|
5374
|
+
check: check ? raw({ raw: check.expression }) : void 0
|
|
5385
5375
|
};
|
|
5376
|
+
const name = item.name && item.name !== getConstraintName(table.name, constraint, ctx.snakeCase) ? item.name : void 0;
|
|
5377
|
+
if (name) {
|
|
5378
|
+
constraint.name = name;
|
|
5379
|
+
if (constraint.references?.options) {
|
|
5380
|
+
constraint.references.options.name = name;
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
return constraint;
|
|
5386
5384
|
};
|
|
5387
|
-
const
|
|
5388
|
-
return
|
|
5385
|
+
const makeIndexOrExcludeOptions = (tableName, index, key) => {
|
|
5386
|
+
return {
|
|
5387
|
+
name: index.name !== (key === "indexes" ? getIndexName : getExcludeName)(
|
|
5388
|
+
tableName,
|
|
5389
|
+
index.columns
|
|
5390
|
+
) ? index.name : void 0,
|
|
5391
|
+
using: index.using === "btree" ? void 0 : index.using,
|
|
5392
|
+
unique: index.unique || void 0,
|
|
5393
|
+
include: index.include,
|
|
5394
|
+
nullsNotDistinct: index.nullsNotDistinct || void 0,
|
|
5395
|
+
with: index.with,
|
|
5396
|
+
tablespace: index.tablespace,
|
|
5397
|
+
where: index.where
|
|
5398
|
+
};
|
|
5389
5399
|
};
|
|
5390
|
-
const
|
|
5391
|
-
const
|
|
5392
|
-
|
|
5400
|
+
const checkIfIsOuterRecursiveFkey = (data, table, references) => {
|
|
5401
|
+
const referencesId = `${references.foreignSchema}.${references.foreignTable}`;
|
|
5402
|
+
const tableId = `${table.schemaName}.${table.name}`;
|
|
5403
|
+
for (const other of data.tables) {
|
|
5404
|
+
const id = `${other.schemaName}.${other.name}`;
|
|
5405
|
+
if (referencesId === id) {
|
|
5406
|
+
for (const c of data.constraints) {
|
|
5407
|
+
if (c.tableName === other.name && c.schemaName === other.schemaName && c.references?.foreignTable === table.name && c.references.foreignSchema === table.schemaName && tableId < id) {
|
|
5408
|
+
return true;
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
break;
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
return false;
|
|
5393
5415
|
};
|
|
5394
5416
|
|
|
5395
5417
|
const pullDbStructure = async (adapter, config) => {
|
|
5396
|
-
const currentSchema = adapter.
|
|
5418
|
+
const currentSchema = adapter.searchPath || "public";
|
|
5397
5419
|
const ctx = makeStructureToAstCtx(config, currentSchema);
|
|
5398
5420
|
const ast = await structureToAst(ctx, adapter, config);
|
|
5399
5421
|
const result = astToMigration(currentSchema, config, ast);
|
|
@@ -5885,5 +5907,5 @@ const makeMigrateAdapter = (config) => {
|
|
|
5885
5907
|
};
|
|
5886
5908
|
};
|
|
5887
5909
|
|
|
5888
|
-
export { RakeDbError, astToMigration, concatSchemaAndName, createMigrationInterface, dbColumnToAst, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getExcludeName, getIndexName, getSchemaAndTableFromName, instantiateDbColumn, introspectDbSchema, makeChange, makeDomainsMap, makeFileVersion, makeMigrateAdapter, makeStructureToAstCtx, migrate, migrateAndClose, migrateFiles, migrationConfigDefaults, processRakeDbConfig, promptSelect, rakeDbCommands, rakeDbWithAdapters, runCommand, saveMigratedVersion, structureToAst, tableToAst, writeMigrationFile };
|
|
5910
|
+
export { RakeDbError, astToMigration, concatSchemaAndName, createMigrationInterface, dbColumnToAst, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, instantiateDbColumn, introspectDbSchema, makeChange, makeDomainsMap, makeFileVersion, makeMigrateAdapter, makeStructureToAstCtx, migrate, migrateAndClose, migrateFiles, migrationConfigDefaults, processRakeDbConfig, promptSelect, rakeDbCommands, rakeDbWithAdapters, runCommand, saveMigratedVersion, structureToAst, tableToAst, writeMigrationFile };
|
|
5889
5911
|
//# sourceMappingURL=index.mjs.map
|