rake-db 2.36.15 → 2.36.17

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
@@ -87,6 +87,8 @@ declare namespace DbStructure {
87
87
  name: string;
88
88
  using: string;
89
89
  unique: boolean;
90
+ /** Deferrability mode when the unique index is backed by a constraint. */
91
+ deferrable?: false | 'immediate' | 'deferred';
90
92
  columns: (({
91
93
  column: string;
92
94
  } | {
package/dist/index.js CHANGED
@@ -266,17 +266,20 @@ const getExcludeName = (table, columns) => getIndexOrExcludeName(table, columns,
266
266
  const indexesToQuery = (up, { schema, name: tableName }, indexes, snakeCase, language) => {
267
267
  return indexes.map((index) => {
268
268
  const { options } = index;
269
+ const isDeferrableUnique = isActiveUniqueDeferrable(options.deferrable);
270
+ if (isDeferrableUnique && !options.unique) throw new Error("Only unique indexes can be deferrable");
269
271
  const { columns, include, name } = getIndexOrExcludeMainOptions(tableName, index, getIndexName, snakeCase);
270
- if (!up) return { text: `DROP INDEX "${name}"${options.dropMode ? ` ${options.dropMode}` : ""}` };
272
+ if (!up) return { text: `${isDeferrableUnique ? `ALTER TABLE ${quoteTable(schema, tableName)} DROP CONSTRAINT "${name}"` : `DROP INDEX "${name}"`}${options.dropMode ? ` ${options.dropMode}` : ""}` };
273
+ if (isDeferrableUnique) validateDeferrableUnique(index);
271
274
  const values = [];
272
- const sql = ["CREATE"];
273
- if (options.unique) sql.push("UNIQUE");
274
- sql.push(`INDEX "${name}" ON ${quoteTable(schema, tableName)}`);
275
+ const sql = isDeferrableUnique ? [`ALTER TABLE ${quoteTable(schema, tableName)} ADD CONSTRAINT "${name}"`, `UNIQUE${options.nullsNotDistinct ? " NULLS NOT DISTINCT" : ""}`] : ["CREATE"];
276
+ if (!isDeferrableUnique && options.unique) sql.push("UNIQUE");
277
+ if (!isDeferrableUnique) sql.push(`INDEX "${name}" ON ${quoteTable(schema, tableName)}`);
275
278
  const u = options.using || options.tsVector && "GIN";
276
- if (u) sql.push(`USING ${u}`);
279
+ if (!isDeferrableUnique && u) sql.push(`USING ${u}`);
277
280
  const lang = options.tsVector && options.languageColumn ? `"${options.languageColumn}"` : options.language ? `'${options.language}'` : `'${language || "english"}'`;
278
- let hasWeight = options.tsVector && columns.some((column) => !!column.weight);
279
- const columnsSql = columns.map((column) => {
281
+ let hasWeight = !isDeferrableUnique && options.tsVector && columns.some((column) => !!column.weight);
282
+ const columnsSql = isDeferrableUnique ? columns.map(uniqueConstraintColumnToSql) : columns.map((column) => {
280
283
  let sql = [
281
284
  "expression" in column ? `(${column.expression})` : `"${column.column}"`,
282
285
  column.collate && `COLLATE ${quoteNameFromString(schema, column.collate)}`,
@@ -298,16 +301,45 @@ const indexesToQuery = (up, { schema, name: tableName }, indexes, snakeCase, lan
298
301
  else columnList = columnsSql.join(", ");
299
302
  sql.push(`(${columnList})`);
300
303
  if (include && include.length) sql.push(`INCLUDE (${include.map((column) => `"${column}"`).join(", ")})`);
301
- if (options.nullsNotDistinct) sql.push(`NULLS NOT DISTINCT`);
304
+ if (!isDeferrableUnique && options.nullsNotDistinct) sql.push(`NULLS NOT DISTINCT`);
302
305
  if (options.with) sql.push(`WITH (${options.with})`);
303
- if (options.tablespace) sql.push(`TABLESPACE ${options.tablespace}`);
304
- if (options.where) sql.push(`WHERE ${(0, pqb_internal.isRawSQL)(options.where) ? options.where.toSQL({ values }) : options.where}`);
306
+ if (options.tablespace) sql.push(isDeferrableUnique ? `USING INDEX TABLESPACE ${options.tablespace}` : `TABLESPACE ${options.tablespace}`);
307
+ if (!isDeferrableUnique && options.where) sql.push(`WHERE ${(0, pqb_internal.isRawSQL)(options.where) ? options.where.toSQL({ values }) : options.where}`);
308
+ if (isDeferrableUnique) sql.push(`DEFERRABLE INITIALLY ${options.deferrable === "deferred" ? "DEFERRED" : "IMMEDIATE"}`);
305
309
  return {
306
310
  text: sql.join(" "),
307
311
  values
308
312
  };
309
313
  });
310
314
  };
315
+ const isActiveUniqueDeferrable = (deferrable) => deferrable === "immediate" || deferrable === "deferred";
316
+ const uniqueConstraintColumnToSql = (column) => {
317
+ if ("expression" in column) throw new Error("Deferrable unique constraints do not support expression indexes");
318
+ return `"${column.column}"`;
319
+ };
320
+ const validateDeferrableUnique = (index) => {
321
+ const { options } = index;
322
+ if (options.where) throw new Error("Deferrable unique constraints do not support partial indexes");
323
+ const unsupportedOption = getUnsupportedDeferrableUniqueIndexOption(options);
324
+ if (unsupportedOption) throw new Error(`Deferrable unique constraints do not support index option: ${unsupportedOption}`);
325
+ for (const column of index.columns) {
326
+ if ("expression" in column) throw new Error("Deferrable unique constraints do not support expression indexes");
327
+ const unsupportedColumnOption = getUnsupportedDeferrableUniqueColumnOption(column);
328
+ if (unsupportedColumnOption) throw new Error(`Deferrable unique constraints do not support index column option: ${unsupportedColumnOption}`);
329
+ }
330
+ };
331
+ const getUnsupportedDeferrableUniqueIndexOption = (options) => {
332
+ if (options.using) return "using";
333
+ if (options.tsVector) return "tsVector";
334
+ if (options.language) return "language";
335
+ if (options.languageColumn) return "languageColumn";
336
+ };
337
+ const getUnsupportedDeferrableUniqueColumnOption = (column) => {
338
+ if (column.collate) return "collate";
339
+ if (column.opclass) return "opclass";
340
+ if (column.order) return "order";
341
+ if (column.weight) return "weight";
342
+ };
311
343
  const excludesToQuery = (up, { schema, name: tableName }, excludes, snakeCase) => {
312
344
  return excludes.map((exclude) => {
313
345
  const { options } = exclude;
@@ -4769,6 +4801,10 @@ const indexesSql = `SELECT
4769
4801
  ic.relname "name",
4770
4802
  am.amname AS "using",
4771
4803
  i.indisunique "unique",
4804
+ CASE
4805
+ WHEN c.condeferrable AND c.condeferred THEN 'deferred'
4806
+ WHEN c.condeferrable THEN 'immediate'
4807
+ END AS "deferrable",
4772
4808
  (
4773
4809
  SELECT json_agg(
4774
4810
  (
@@ -4865,6 +4901,9 @@ JOIN pg_class t ON t.oid = i.indrelid
4865
4901
  JOIN pg_namespace n ON n.oid = t.relnamespace
4866
4902
  JOIN pg_class ic ON ic.oid = i.indexrelid
4867
4903
  JOIN pg_am am ON am.oid = ic.relam
4904
+ LEFT JOIN pg_constraint c
4905
+ ON c.conindid = ic.oid
4906
+ AND c.contype = 'u'
4868
4907
  LEFT JOIN pg_catalog.pg_class tc ON (ic.reltoastrelid = tc.oid)
4869
4908
  WHERE ${filterSchema("n.nspname")}
4870
4909
  AND NOT i.indisprimary
@@ -5218,6 +5257,7 @@ async function introspectDbSchema(db, params) {
5218
5257
  const excludes = [];
5219
5258
  for (const index of raw.indexes) {
5220
5259
  nullsToUndefined(index);
5260
+ if (!index.deferrable) index.deferrable = void 0;
5221
5261
  for (const column of index.columns) {
5222
5262
  if (!("expression" in column)) continue;
5223
5263
  const s = column.expression;
@@ -5523,6 +5563,8 @@ const instantiateDbColumn = (ctx, data, domains, dbColumn) => {
5523
5563
  const arr = new pqb_internal.ArrayColumn(ctx.columnSchemaConfig, column, ctx.columnSchemaConfig.__schemaType);
5524
5564
  arr.data.isNullable = dbColumn.isNullable;
5525
5565
  arr.data.arrayDims = dbColumn.arrayDims;
5566
+ arr.data.default = column.data.default;
5567
+ column.data.default = void 0;
5526
5568
  column = arr;
5527
5569
  }
5528
5570
  return column;
@@ -5767,6 +5809,7 @@ const makeIndexOrExcludeOptions = (tableName, index, key) => {
5767
5809
  name: index.name !== (key === "indexes" ? getIndexName : getExcludeName)(tableName, index.columns) ? index.name : void 0,
5768
5810
  using: index.using === "btree" ? void 0 : index.using,
5769
5811
  unique: index.unique || void 0,
5812
+ deferrable: index.deferrable || void 0,
5770
5813
  include: index.include,
5771
5814
  nullsNotDistinct: index.nullsNotDistinct || void 0,
5772
5815
  with: index.with,