rake-db 2.4.8 → 2.4.10

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 CHANGED
@@ -85,14 +85,16 @@ declare class MigrationBase {
85
85
  dropSchema(schemaName: string): Promise<void>;
86
86
  createExtension(name: string, options?: Omit<RakeDbAst.Extension, 'type' | 'action' | 'name'>): Promise<void>;
87
87
  dropExtension(name: string, options?: Omit<RakeDbAst.Extension, 'type' | 'action' | 'name' | 'values'>): Promise<void>;
88
- createEnum(name: string, values: [string, ...string[]], options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values'>): Promise<void>;
89
- dropEnum(name: string, values: [string, ...string[]], options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values'>): Promise<void>;
88
+ createEnum(name: string, values: [string, ...string[]], options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values' | 'schema'>): Promise<void>;
89
+ dropEnum(name: string, values: [string, ...string[]], options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values' | 'schema'>): Promise<void>;
90
+ createDomain(name: string, fn: (t: ColumnTypes) => ColumnType, options?: Omit<RakeDbAst.Domain, 'type' | 'action' | 'schema' | 'name' | 'baseType'>): Promise<void>;
91
+ dropDomain(name: string, fn: (t: ColumnTypes) => ColumnType, options?: Omit<RakeDbAst.Domain, 'type' | 'action' | 'schema' | 'name' | 'baseType'>): Promise<void>;
90
92
  tableExists(tableName: string): Promise<boolean>;
91
93
  columnExists(tableName: string, columnName: string): Promise<boolean>;
92
94
  constraintExists(constraintName: string): Promise<boolean>;
93
95
  }
94
96
 
95
- declare type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameTable | RakeDbAst.Schema | RakeDbAst.Extension | RakeDbAst.Enum | RakeDbAst.ForeignKey;
97
+ declare type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameTable | RakeDbAst.Schema | RakeDbAst.Extension | RakeDbAst.Enum | RakeDbAst.Domain | RakeDbAst.ForeignKey;
96
98
  declare namespace RakeDbAst {
97
99
  type Table = {
98
100
  type: 'table';
@@ -179,6 +181,18 @@ declare namespace RakeDbAst {
179
181
  cascade?: boolean;
180
182
  dropIfExists?: boolean;
181
183
  };
184
+ type Domain = {
185
+ type: 'domain';
186
+ action: 'create' | 'drop';
187
+ schema?: string;
188
+ name: string;
189
+ baseType: ColumnType;
190
+ notNull?: boolean;
191
+ collation?: string;
192
+ default?: RawExpression;
193
+ check?: RawExpression;
194
+ cascade?: boolean;
195
+ };
182
196
  type EnumOptions = {
183
197
  createIfNotExists?: boolean;
184
198
  dropIfExists?: boolean;
package/dist/index.js CHANGED
@@ -302,8 +302,11 @@ var __spreadValues$5 = (a, b) => {
302
302
  return a;
303
303
  };
304
304
  var __spreadProps$4 = (a, b) => __defProps$4(a, __getOwnPropDescs$4(b));
305
+ const columnTypeToSql = (item) => {
306
+ return item.data.isOfCustomType ? `"${item.toSQL()}"` : item.toSQL();
307
+ };
305
308
  const columnToSql = (key, item, values, hasMultiplePrimaryKeys) => {
306
- const line = [`"${item.data.name || key}" ${item.toSQL()}`];
309
+ const line = [`"${item.data.name || key}" ${columnTypeToSql(item)}`];
307
310
  if (item.data.compression) {
308
311
  line.push(`COMPRESSION ${item.data.compression}`);
309
312
  }
@@ -508,7 +511,7 @@ var __spreadValues$4 = (a, b) => {
508
511
  return a;
509
512
  };
510
513
  var __spreadProps$3 = (a, b) => __defProps$3(a, __getOwnPropDescs$3(b));
511
- var __objRest = (source, exclude) => {
514
+ var __objRest$1 = (source, exclude) => {
512
515
  var target = {};
513
516
  for (var prop in source)
514
517
  if (__hasOwnProp$4.call(source, prop) && exclude.indexOf(prop) < 0)
@@ -536,7 +539,7 @@ const createTable$1 = async (migration, up, tableName, options, fn) => {
536
539
  validatePrimaryKey(ast);
537
540
  const queries = astToQueries$1(ast);
538
541
  for (const _a of queries) {
539
- const _b = _a, { then } = _b, query = __objRest(_b, ["then"]);
542
+ const _b = _a, { then } = _b, query = __objRest$1(_b, ["then"]);
540
543
  const result = await migration.adapter.arrays(query);
541
544
  then == null ? void 0 : then(result);
542
545
  }
@@ -917,8 +920,9 @@ const astToQueries = (ast) => {
917
920
  } else if (item.type === "change") {
918
921
  const { from, to } = item;
919
922
  if (to.type && (from.type !== to.type || from.collate !== to.collate)) {
923
+ const type = !to.column || to.column.data.isOfCustomType ? `"${to.type}"` : to.type;
920
924
  alterTable.push(
921
- `ALTER COLUMN "${item.name || key}" TYPE ${to.type}${to.collate ? ` COLLATE ${pqb.quote(to.collate)}` : ""}${item.using ? ` USING ${pqb.getRaw(item.using, values)}` : ""}`
925
+ `ALTER COLUMN "${item.name || key}" TYPE ${type}${to.collate ? ` COLLATE ${pqb.quote(to.collate)}` : ""}${item.using ? ` USING ${pqb.getRaw(item.using, values)}` : ""}`
922
926
  );
923
927
  }
924
928
  if (from.default !== to.default) {
@@ -1186,6 +1190,12 @@ class MigrationBase {
1186
1190
  dropEnum(name, values, options) {
1187
1191
  return createEnum$1(this, !this.up, name, values, options);
1188
1192
  }
1193
+ createDomain(name, fn, options) {
1194
+ return createDomain$1(this, this.up, name, fn, options);
1195
+ }
1196
+ dropDomain(name, fn, options) {
1197
+ return createDomain$1(this, !this.up, name, fn, options);
1198
+ }
1189
1199
  async tableExists(tableName) {
1190
1200
  return queryExists(this, {
1191
1201
  text: `SELECT 1 FROM "information_schema"."tables" WHERE "table_name" = $1`,
@@ -1280,6 +1290,34 @@ const createEnum$1 = async (migration, up, name, values, options = {}) => {
1280
1290
  await migration.adapter.query(query);
1281
1291
  migration.migratedAsts.push(ast);
1282
1292
  };
1293
+ const createDomain$1 = async (migration, up, name, fn, options) => {
1294
+ const [schema, domainName] = getSchemaAndTableFromName(name);
1295
+ const ast = __spreadValues$2({
1296
+ type: "domain",
1297
+ action: up ? "create" : "drop",
1298
+ schema,
1299
+ name: domainName,
1300
+ baseType: fn(pqb.columnTypes)
1301
+ }, options);
1302
+ let query;
1303
+ const values = [];
1304
+ const quotedName = quoteWithSchema(ast);
1305
+ if (ast.action === "create") {
1306
+ query = `CREATE DOMAIN ${quotedName} AS ${columnTypeToSql(ast.baseType)}${ast.collation ? `
1307
+ COLLATION ${orchidCore.singleQuote(ast.collation)}` : ""}${ast.default ? `
1308
+ DEFAULT ${pqb.getRaw(ast.default, values)}` : ""}${ast.notNull || ast.check ? "\n" : ""}${[
1309
+ ast.notNull && "NOT NULL",
1310
+ ast.check && `CHECK ${pqb.getRaw(ast.check, values)}`
1311
+ ].filter(Boolean).join(" ")}`;
1312
+ } else {
1313
+ query = `DROP DOMAIN ${quotedName}${ast.cascade ? " CASCADE" : ""}`;
1314
+ }
1315
+ await migration.adapter.query({
1316
+ text: query,
1317
+ values
1318
+ });
1319
+ migration.migratedAsts.push(ast);
1320
+ };
1283
1321
  const queryExists = (db, sql) => {
1284
1322
  return db.adapter.query(sql).then(({ rowCount }) => rowCount > 0);
1285
1323
  };
@@ -1923,6 +1961,38 @@ ORDER BY c.conname`);
1923
1961
  }
1924
1962
  return rows;
1925
1963
  }
1964
+ async getDomains() {
1965
+ const { rows } = await this.db.query(`SELECT
1966
+ n.nspname AS "schemaName",
1967
+ d.typname AS "name",
1968
+ t.typname AS "type",
1969
+ s.nspname AS "typeSchema",
1970
+ .typnotnull AS "notNull",
1971
+ d.typcategory = 'A' AS "isArray",
1972
+ character_maximum_length AS "maxChars",
1973
+ numeric_precision AS "numericPrecision",
1974
+ numeric_scale AS "numericScale",
1975
+ datetime_precision AS "dateTimePrecision",
1976
+ collation_name AS "collation",
1977
+ domain_default AS "default",
1978
+ pg_get_expr(conbin, conrelid) AS "expression"
1979
+ FROM pg_catalog.pg_type d
1980
+ JOIN pg_catalog.pg_namespace n ON n.oid = d.typnamespace
1981
+ JOIN information_schema.domains i
1982
+ ON i.domain_schema = nspname
1983
+ AND i.domain_name = d.typname
1984
+ JOIN pg_catalog.pg_type t
1985
+ ON (
1986
+ CASE WHEN d.typcategory = 'A'
1987
+ THEN t.typarray
1988
+ ELSE t.oid
1989
+ END
1990
+ ) = d.typbasetype
1991
+ JOIN pg_catalog.pg_namespace s ON s.oid = t.typnamespace
1992
+ LEFT JOIN pg_catalog.pg_constraint c ON c.contypid = d.oid
1993
+ WHERE d.typtype = 'd' AND ${filterSchema("n.nspname")}`);
1994
+ return rows;
1995
+ }
1926
1996
  }
1927
1997
 
1928
1998
  var __defProp = Object.defineProperty;
@@ -1944,6 +2014,18 @@ var __spreadValues = (a, b) => {
1944
2014
  return a;
1945
2015
  };
1946
2016
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
2017
+ var __objRest = (source, exclude) => {
2018
+ var target = {};
2019
+ for (var prop in source)
2020
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
2021
+ target[prop] = source[prop];
2022
+ if (source != null && __getOwnPropSymbols)
2023
+ for (var prop of __getOwnPropSymbols(source)) {
2024
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
2025
+ target[prop] = source[prop];
2026
+ }
2027
+ return target;
2028
+ };
1947
2029
  class RakeDbEnumColumn extends pqb.EnumColumn {
1948
2030
  toCode(t) {
1949
2031
  return pqb.columnCode(this, t, `enum('${this.enumName}')`);
@@ -1961,7 +2043,7 @@ const fkeyActionMap = {
1961
2043
  n: "SET NULL",
1962
2044
  d: "SET DEFAULT"
1963
2045
  };
1964
- const structureToAst = async (db) => {
2046
+ const structureToAst = async (config, db) => {
1965
2047
  const ast = [];
1966
2048
  const data = await getData(db);
1967
2049
  for (const name of data.schemas) {
@@ -1987,10 +2069,21 @@ const structureToAst = async (db) => {
1987
2069
  }
1988
2070
  pendingTables[key] = { table, dependsOn };
1989
2071
  }
2072
+ const domains = {};
2073
+ for (const it of data.domains) {
2074
+ domains[`${it.schemaName}.${it.name}`] = getColumn(config, data, domains, {
2075
+ schemaName: it.schemaName,
2076
+ name: it.name,
2077
+ type: it.type,
2078
+ typeSchema: it.typeSchema,
2079
+ isArray: it.isArray,
2080
+ isSerial: false
2081
+ });
2082
+ }
1990
2083
  for (const key in pendingTables) {
1991
2084
  const { table, dependsOn } = pendingTables[key];
1992
2085
  if (!dependsOn.size) {
1993
- pushTableAst(ast, data, table, pendingTables);
2086
+ pushTableAst(config, ast, data, domains, table, pendingTables);
1994
2087
  }
1995
2088
  }
1996
2089
  const outerFKeys = [];
@@ -2012,6 +2105,19 @@ const structureToAst = async (db) => {
2012
2105
  values: it.values
2013
2106
  });
2014
2107
  }
2108
+ for (const it of data.domains) {
2109
+ ast.push({
2110
+ type: "domain",
2111
+ action: "create",
2112
+ schema: it.schemaName === "public" ? void 0 : it.schemaName,
2113
+ name: it.name,
2114
+ baseType: domains[`${it.schemaName}.${it.name}`],
2115
+ notNull: it.notNull,
2116
+ collation: it.collation,
2117
+ default: it.default ? orchidCore.raw(it.default) : void 0,
2118
+ check: it.check ? orchidCore.raw(it.check) : void 0
2119
+ });
2120
+ }
2015
2121
  for (const key in pendingTables) {
2016
2122
  const innerFKeys = [];
2017
2123
  const { table } = pendingTables[key];
@@ -2025,7 +2131,7 @@ const structureToAst = async (db) => {
2025
2131
  outerFKeys.push([fkey, table]);
2026
2132
  }
2027
2133
  }
2028
- pushTableAst(ast, data, table, pendingTables, innerFKeys);
2134
+ pushTableAst(config, ast, data, domains, table, pendingTables, innerFKeys);
2029
2135
  }
2030
2136
  for (const [fkey, table] of outerFKeys) {
2031
2137
  ast.push(__spreadProps(__spreadValues({}, foreignKeyToAst(fkey)), {
@@ -2047,7 +2153,8 @@ const getData = async (db) => {
2047
2153
  foreignKeys,
2048
2154
  extensions,
2049
2155
  enums,
2050
- checks
2156
+ checks,
2157
+ domains
2051
2158
  ] = await Promise.all([
2052
2159
  db.getSchemas(),
2053
2160
  db.getTables(),
@@ -2057,7 +2164,8 @@ const getData = async (db) => {
2057
2164
  db.getForeignKeys(),
2058
2165
  db.getExtensions(),
2059
2166
  db.getEnums(),
2060
- db.getChecks()
2167
+ db.getChecks(),
2168
+ db.getDomains()
2061
2169
  ]);
2062
2170
  return {
2063
2171
  schemas,
@@ -2068,7 +2176,8 @@ const getData = async (db) => {
2068
2176
  foreignKeys,
2069
2177
  extensions,
2070
2178
  enums,
2071
- checks
2179
+ checks,
2180
+ domains
2072
2181
  };
2073
2182
  };
2074
2183
  const makeBelongsToTable = (schema, table) => (item) => item.schemaName === schema && item.tableName === table;
@@ -2082,12 +2191,55 @@ const getIsSerial = (item) => {
2082
2191
  }
2083
2192
  return false;
2084
2193
  };
2194
+ const getColumn = (config, data, domains, _a) => {
2195
+ var _b = _a, {
2196
+ schemaName,
2197
+ tableName,
2198
+ name,
2199
+ type,
2200
+ typeSchema,
2201
+ isArray,
2202
+ isSerial
2203
+ } = _b, params = __objRest(_b, [
2204
+ "schemaName",
2205
+ "tableName",
2206
+ "name",
2207
+ "type",
2208
+ "typeSchema",
2209
+ "isArray",
2210
+ "isSerial"
2211
+ ]);
2212
+ var _a2;
2213
+ let column;
2214
+ const klass = pqb.columnsByType[getColumnType(type, isSerial)];
2215
+ if (klass) {
2216
+ column = pqb.instantiateColumn(klass, params);
2217
+ } else {
2218
+ const domainColumn = domains[`${typeSchema}.${type}`];
2219
+ if (domainColumn) {
2220
+ column = new pqb.DomainColumn({}, type).as(domainColumn);
2221
+ } else {
2222
+ const enumType = data.enums.find(
2223
+ (item) => item.name === type && item.schemaName === typeSchema
2224
+ );
2225
+ if (enumType) {
2226
+ column = new RakeDbEnumColumn({}, type, enumType.values);
2227
+ } else {
2228
+ column = new pqb.CustomTypeColumn({}, type);
2229
+ (_a2 = config.logger) == null ? void 0 : _a2.warn(
2230
+ `${tableName ? "Column" : "Domain"} ${schemaName}${tableName ? `.${tableName}` : ""}.${name} has unsupported type \`${type}\`, append \`as\` method manually to it to treat it as other column type`
2231
+ );
2232
+ }
2233
+ }
2234
+ }
2235
+ return isArray ? new pqb.ArrayColumn({}, column) : column;
2236
+ };
2085
2237
  const getColumnType = (type, isSerial) => {
2086
2238
  if (!isSerial)
2087
2239
  return type;
2088
2240
  return type === "int2" ? "smallserial" : type === "int4" ? "serial" : "bigserial";
2089
2241
  };
2090
- const pushTableAst = (ast, data, table, pendingTables, innerFKeys = data.foreignKeys) => {
2242
+ const pushTableAst = (config, ast, data, domains, table, pendingTables, innerFKeys = data.foreignKeys) => {
2091
2243
  const { schemaName, name } = table;
2092
2244
  const key = `${schemaName}.${table.name}`;
2093
2245
  delete pendingTables[key];
@@ -2110,26 +2262,12 @@ const pushTableAst = (ast, data, table, pendingTables, innerFKeys = data.foreign
2110
2262
  if (isSerial) {
2111
2263
  item = __spreadProps(__spreadValues({}, item), { default: void 0 });
2112
2264
  }
2113
- let column;
2114
2265
  const isArray = item.dataType === "ARRAY";
2115
- const type = isArray ? item.type.slice(1) : item.type;
2116
- const klass = pqb.columnsByType[getColumnType(type, isSerial)];
2117
- if (klass) {
2118
- column = pqb.instantiateColumn(klass, item);
2119
- } else {
2120
- const { type: type2, typeSchema } = item;
2121
- const enumType = data.enums.find(
2122
- (item2) => item2.name === type2 && item2.schemaName === typeSchema
2123
- );
2124
- if (!enumType) {
2125
- throw new Error(
2126
- `Cannot handle column ${item.schemaName}.${item.tableName}.${item.name}: column type \`${item.type}\` is not supported`
2127
- );
2128
- }
2129
- column = new RakeDbEnumColumn({}, type2, enumType.values);
2130
- }
2131
- if (isArray)
2132
- column = new pqb.ArrayColumn({}, column);
2266
+ let column = getColumn(config, data, domains, __spreadProps(__spreadValues({}, item), {
2267
+ type: isArray ? item.type.slice(1) : item.type,
2268
+ isArray,
2269
+ isSerial
2270
+ }));
2133
2271
  if ((primaryKey == null ? void 0 : primaryKey.columnNames.length) === 1 && (primaryKey == null ? void 0 : primaryKey.columnNames[0]) === item.name) {
2134
2272
  column = column.primaryKey();
2135
2273
  }
@@ -2208,7 +2346,7 @@ const pushTableAst = (ast, data, table, pendingTables, innerFKeys = data.foreign
2208
2346
  for (const otherKey in pendingTables) {
2209
2347
  const item = pendingTables[otherKey];
2210
2348
  if (item.dependsOn.delete(key) && item.dependsOn.size === 0) {
2211
- pushTableAst(ast, data, item.table, pendingTables);
2349
+ pushTableAst(config, ast, data, domains, item.table, pendingTables);
2212
2350
  }
2213
2351
  }
2214
2352
  };
@@ -2238,7 +2376,11 @@ const astToMigration = (config, ast) => {
2238
2376
  } else if (item.type === "enum" && item.action === "create") {
2239
2377
  if (first.length)
2240
2378
  first.push([]);
2241
- first.push(...createEnum(item));
2379
+ first.push(createEnum(item));
2380
+ } else if (item.type === "domain" && item.action === "create") {
2381
+ if (first.length)
2382
+ first.push([]);
2383
+ first.push(...createDomain(item));
2242
2384
  } else if (item.type === "table" && item.action === "create") {
2243
2385
  tables.push(createTable(config, item));
2244
2386
  } else if (item.type === "foreignKey") {
@@ -2295,12 +2437,26 @@ const createExtension = (ast) => {
2295
2437
  return code;
2296
2438
  };
2297
2439
  const createEnum = (ast) => {
2440
+ return `await db.createEnum(${quoteSchemaTable(ast)}, [${ast.values.map(orchidCore.singleQuote).join(", ")}]);`;
2441
+ };
2442
+ const createDomain = (ast) => {
2298
2443
  const code = [
2299
- `await db.createEnum(${orchidCore.singleQuote(ast.name)}, [${ast.values.map(orchidCore.singleQuote).join(", ")}]`
2444
+ `await db.createDomain(${quoteSchemaTable(
2445
+ ast
2446
+ )}, (t) => ${ast.baseType.toCode("t")}`
2300
2447
  ];
2301
- if (ast.schema) {
2448
+ if (ast.notNull || ast.collation || ast.default || ast.check) {
2449
+ const props = [];
2450
+ if (ast.notNull)
2451
+ props.push(`notNull: true,`);
2452
+ if (ast.collation)
2453
+ props.push(`collation: ${orchidCore.singleQuote(ast.collation)},`);
2454
+ if (ast.default)
2455
+ props.push(`default: ${pqb.rawToCode("db", ast.default)},`);
2456
+ if (ast.check)
2457
+ props.push(`check: ${pqb.rawToCode("db", ast.check)},`);
2302
2458
  orchidCore.addCode(code, ", {");
2303
- code.push([`schema: ${orchidCore.singleQuote(ast.schema)},`]);
2459
+ code.push(props);
2304
2460
  orchidCore.addCode(code, "}");
2305
2461
  }
2306
2462
  orchidCore.addCode(code, ");");
@@ -2365,7 +2521,7 @@ const pullDbStructure = async (options, config) => {
2365
2521
  var _a;
2366
2522
  const adapter = new pqb.Adapter(options);
2367
2523
  const db = new DbStructure(adapter);
2368
- const ast = await structureToAst(db);
2524
+ const ast = await structureToAst(config, db);
2369
2525
  await adapter.close();
2370
2526
  const result = astToMigration(config, ast);
2371
2527
  if (!result)