joist-migration-utils 2.3.0-next.7 → 2.3.0-next.71

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.
@@ -0,0 +1,97 @@
1
+ import { ColumnDefinition, ColumnDefinitions, DropOptions, MigrationBuilder, TableOptions } from "node-pg-migrate";
2
+ //#region src/utils.d.ts
3
+ /**
4
+ * Creates an entity table with our conventions.
5
+ *
6
+ * Specifically an `id` auto-increment column (via a sequence) and `created_at` and `updated_at`
7
+ * columns.
8
+ */
9
+ declare function createEntityTable(b: MigrationBuilder, tableName: string, columns: ColumnDefinitions): void;
10
+ /**
11
+ * Creates a subtype table using class-per-table inheritance.
12
+ *
13
+ * The subtable will use the base table's id as its identity, and when loading rows of the base
14
+ * type, Joist will automatically stitch together rows across each table into a single instance.
15
+ */
16
+ declare function createSubTable(b: MigrationBuilder, baseTableName: string, subTableName: string, columns: ColumnDefinitions): void;
17
+ declare function createEnumTable(b: MigrationBuilder, tableName: string, values: Array<[string, string]>): void;
18
+ declare function addEnumValue(b: MigrationBuilder, tableName: string, value: [string, string]): void;
19
+ declare function updateEnumValue(b: MigrationBuilder, tableName: string, previousCode: string, value: [string, string]): void;
20
+ /** Makes a trigger to update the `updated_at` column. */
21
+ declare function createTriggers(b: MigrationBuilder, tableName: string): void;
22
+ declare function createUpdatedAtFunction(b: MigrationBuilder): void;
23
+ /**
24
+ * Unnests a 2d array like `[[a, b], [c, d]]` into two rows of `[a, b]` and `[c, d]`.
25
+ *
26
+ * This custom function is a key part of Joist's bulk INSERT/UPDATE that sends "all firstNames as
27
+ * a single param", because it lets us support array columns as a single parameter (i.e. an array
28
+ * of arrays).
29
+ *
30
+ * Postgres's built-in `unnest` is too aggressive when flattening, and will unnest the 2d array
31
+ * of `[[a, b], [c, d]]` into four rows of `a`, `b`, `c`, and `d`, which is not what we want.
32
+ *
33
+ * Additionally, Postgres does not support "jagged arrays", i.e. 2D arrays where some
34
+ * sub-arrays are longer than others, e.g. `[[a, b], [c, d, e]]`. Instead, it treats arrays
35
+ * more as the mathematical concept of a matrix, than "a list of lists".
36
+ *
37
+ * To work around this, our PostgresDriver auto-pads all 2D arrays to the same length, by
38
+ * appending `NULL`, and then `unnest_arrays` prunes all `NULL`s from the resulting 1D arrays.
39
+ * See `fillArrayWithNulls`.
40
+ *
41
+ *
42
+ * This means we purposefully do not/cannot support `NULL` values _within_ array columns,
43
+ * i.e. `nick_names=[a, NULL, b]` (without some additional heuristics).
44
+ *
45
+ * That said, we do need to support nullable array columns, i.e. `nick_names=NULL`, which we
46
+ * detect by prefixing an additional marker element to each sub-array that indicates whether the
47
+ * array column is null or not, and then conditionally enabling this with the `nullable=true`
48
+ * param to `unnest_arrays`.
49
+ */
50
+ declare const unnest_arrays = "\nCREATE OR REPLACE FUNCTION unnest_arrays(arr ANYARRAY, nullable BOOLEAN = false, OUT a ANYARRAY)\n RETURNS SETOF ANYARRAY\n LANGUAGE plpgsql IMMUTABLE STRICT AS\n$func$\nBEGIN\n FOREACH a SLICE 1 IN ARRAY arr LOOP\n IF nullable THEN\n IF a[1] IS NULL THEN a := NULL;\n ELSE a := a[2:array_length(a, 1)];\n END IF;\n END IF;\n a := array_remove(a, NULL);\n RETURN NEXT;\n END LOOP;\nEND\n$func$;\n";
51
+ declare function createUnnestArraysFunction(b: MigrationBuilder): void;
52
+ declare function createCreatedAtFunction(b: MigrationBuilder): void;
53
+ type FieldNameOverrides = {
54
+ fieldName?: string;
55
+ otherFieldName?: string;
56
+ };
57
+ type ForeignKeyOpts = Partial<ColumnDefinition> & Required<Pick<ColumnDefinition, "notNull">> & FieldNameOverrides;
58
+ declare function foreignKey(otherTable: string, opts: ForeignKeyOpts): ColumnDefinition;
59
+ type RenameRelationOpts = FieldNameOverrides & Pick<ColumnDefinition, "comment">;
60
+ declare function renameRelation(b: MigrationBuilder, tableName: string, columnName: string, opts: RenameRelationOpts): void;
61
+ declare function commentData(data: any, comment?: string | null): string;
62
+ declare function enumArrayColumn(enumTable: string, opts?: Pick<ColumnDefinition, "notNull">): ColumnDefinition;
63
+ type ManyToManyColumn = {
64
+ /** The target table for this m2m table, i.e. for a `books_to_tags`, this might be `books`. */
65
+ table: string;
66
+ /** The column name within the m2m table, i.e. for a `books_to_tags`, this might be `book_id`. */
67
+ column?: string;
68
+ /**
69
+ * The name of the collection that *points to* these rows, i.e. for `books_to_tags` and the `book_id`
70
+ * column, `collectionName=taggedBooks` b/c `t1.taggedBooks` does a `SELECT book_id WHERE tag_id=t:1`.
71
+ *
72
+ * For self-referential m2m tables, i.e. `author_to_mentors`, these names can be confusing, i.e.:
73
+ *
74
+ * ```
75
+ * createManyToManyTable(
76
+ * b,
77
+ * "author_to_mentors",
78
+ * // column=mentor_id, collectionName=mentors ==> `a.mentors` does `select mentor_id WHERE mentee_id=a:1`
79
+ * { table: "authors", column: "mentor_id", collectionName: "mentors" },
80
+ * // column=mentee_id, collectionName=mentees ==> `a.mentees` does `select mentee_id WHERE mentor_id=a:1`
81
+ * { table: "authors", column: "mentee_id", collectionName: "mentees" },
82
+ * );
83
+ * ````
84
+ */
85
+ collectionName?: string;
86
+ };
87
+ /** Creates a many-to-many table between `table1` and `table2` with our conventions. */
88
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, table1: string, table2: string, options?: TableOptions & DropOptions): void;
89
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, column1: ManyToManyColumn, column2: ManyToManyColumn, options?: TableOptions & DropOptions): void;
90
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, table1: string, column2: ManyToManyColumn, options?: TableOptions & DropOptions): void;
91
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, column1: ManyToManyColumn, table2: string, options?: TableOptions & DropOptions): void;
92
+ /** Adds columns + auto-indexes any foreign keys. */
93
+ declare function addColumns(b: MigrationBuilder, tableName: string, columns: ColumnDefinitions): void;
94
+ declare function fail(message?: string): never;
95
+ //#endregion
96
+ export { FieldNameOverrides, RenameRelationOpts, addColumns, addEnumValue, commentData, createCreatedAtFunction, createEntityTable, createEnumTable, createManyToManyTable, createSubTable, createTriggers, createUnnestArraysFunction, createUpdatedAtFunction, enumArrayColumn, fail, foreignKey, renameRelation, unnest_arrays, updateEnumValue };
97
+ //# sourceMappingURL=utils.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.cts","names":[],"sources":["../src/utils.ts"],"mappings":";;;;;;;;iBAkBgB,kBAAkB,GAAG,kBAAkB,mBAAmB,SAAS;;;;;;;iBAyBnE,eACd,GAAG,kBACH,uBACA,sBACA,SAAS;iBAsBK,gBAAgB,GAAG,kBAAkB,mBAAmB,QAAQ;iBAShE,aAAa,GAAG,kBAAkB,mBAAmB;iBAMrD,gBACd,GAAG,kBACH,mBACA,sBACA;;iBAcc,eAAe,GAAG,kBAAkB;iBAepC,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoC9B;iBAmBG,2BAA2B,GAAG;iBAI9B,wBAAwB,GAAG;KAS/B;EACV;EACA;;KAEG,iBAAiB,QAAQ,oBAAoB,SAAS,KAAK,gCAAgC;iBAChF,WAAW,oBAAoB,MAAM,iBAAiB;KAU1D,qBAAqB,qBAAqB,KAAK;iBAC3C,eAAe,GAAG,kBAAkB,mBAAmB,oBAAoB,MAAM;iBAIjF,YAAY,WAAW;iBAkBvB,gBAAgB,mBAAmB,OAAO,KAAK,+BAA+B;KAazF;;EAEH;;EAEA;;;;;;;;;;;;;;;;;;EAkBA;;;iBAac,sBACd,GAAG,kBACH,mBACA,gBACA,gBACA,UAAU,eAAe;iBAEX,sBACd,GAAG,kBACH,mBACA,SAAS,kBACT,SAAS,kBACT,UAAU,eAAe;iBAEX,sBACd,GAAG,kBACH,mBACA,gBACA,SAAS,kBACT,UAAU,eAAe;iBAEX,sBACd,GAAG,kBACH,mBACA,SAAS,kBACT,gBACA,UAAU,eAAe;;iBA0BX,WAAW,GAAG,kBAAkB,mBAAmB,SAAS;iBAS5D,KAAK"}
@@ -0,0 +1,97 @@
1
+ import { ColumnDefinition, ColumnDefinitions, DropOptions, MigrationBuilder, TableOptions } from "node-pg-migrate";
2
+ //#region src/utils.d.ts
3
+ /**
4
+ * Creates an entity table with our conventions.
5
+ *
6
+ * Specifically an `id` auto-increment column (via a sequence) and `created_at` and `updated_at`
7
+ * columns.
8
+ */
9
+ declare function createEntityTable(b: MigrationBuilder, tableName: string, columns: ColumnDefinitions): void;
10
+ /**
11
+ * Creates a subtype table using class-per-table inheritance.
12
+ *
13
+ * The subtable will use the base table's id as its identity, and when loading rows of the base
14
+ * type, Joist will automatically stitch together rows across each table into a single instance.
15
+ */
16
+ declare function createSubTable(b: MigrationBuilder, baseTableName: string, subTableName: string, columns: ColumnDefinitions): void;
17
+ declare function createEnumTable(b: MigrationBuilder, tableName: string, values: Array<[string, string]>): void;
18
+ declare function addEnumValue(b: MigrationBuilder, tableName: string, value: [string, string]): void;
19
+ declare function updateEnumValue(b: MigrationBuilder, tableName: string, previousCode: string, value: [string, string]): void;
20
+ /** Makes a trigger to update the `updated_at` column. */
21
+ declare function createTriggers(b: MigrationBuilder, tableName: string): void;
22
+ declare function createUpdatedAtFunction(b: MigrationBuilder): void;
23
+ /**
24
+ * Unnests a 2d array like `[[a, b], [c, d]]` into two rows of `[a, b]` and `[c, d]`.
25
+ *
26
+ * This custom function is a key part of Joist's bulk INSERT/UPDATE that sends "all firstNames as
27
+ * a single param", because it lets us support array columns as a single parameter (i.e. an array
28
+ * of arrays).
29
+ *
30
+ * Postgres's built-in `unnest` is too aggressive when flattening, and will unnest the 2d array
31
+ * of `[[a, b], [c, d]]` into four rows of `a`, `b`, `c`, and `d`, which is not what we want.
32
+ *
33
+ * Additionally, Postgres does not support "jagged arrays", i.e. 2D arrays where some
34
+ * sub-arrays are longer than others, e.g. `[[a, b], [c, d, e]]`. Instead, it treats arrays
35
+ * more as the mathematical concept of a matrix, than "a list of lists".
36
+ *
37
+ * To work around this, our PostgresDriver auto-pads all 2D arrays to the same length, by
38
+ * appending `NULL`, and then `unnest_arrays` prunes all `NULL`s from the resulting 1D arrays.
39
+ * See `fillArrayWithNulls`.
40
+ *
41
+ *
42
+ * This means we purposefully do not/cannot support `NULL` values _within_ array columns,
43
+ * i.e. `nick_names=[a, NULL, b]` (without some additional heuristics).
44
+ *
45
+ * That said, we do need to support nullable array columns, i.e. `nick_names=NULL`, which we
46
+ * detect by prefixing an additional marker element to each sub-array that indicates whether the
47
+ * array column is null or not, and then conditionally enabling this with the `nullable=true`
48
+ * param to `unnest_arrays`.
49
+ */
50
+ declare const unnest_arrays = "\nCREATE OR REPLACE FUNCTION unnest_arrays(arr ANYARRAY, nullable BOOLEAN = false, OUT a ANYARRAY)\n RETURNS SETOF ANYARRAY\n LANGUAGE plpgsql IMMUTABLE STRICT AS\n$func$\nBEGIN\n FOREACH a SLICE 1 IN ARRAY arr LOOP\n IF nullable THEN\n IF a[1] IS NULL THEN a := NULL;\n ELSE a := a[2:array_length(a, 1)];\n END IF;\n END IF;\n a := array_remove(a, NULL);\n RETURN NEXT;\n END LOOP;\nEND\n$func$;\n";
51
+ declare function createUnnestArraysFunction(b: MigrationBuilder): void;
52
+ declare function createCreatedAtFunction(b: MigrationBuilder): void;
53
+ type FieldNameOverrides = {
54
+ fieldName?: string;
55
+ otherFieldName?: string;
56
+ };
57
+ type ForeignKeyOpts = Partial<ColumnDefinition> & Required<Pick<ColumnDefinition, "notNull">> & FieldNameOverrides;
58
+ declare function foreignKey(otherTable: string, opts: ForeignKeyOpts): ColumnDefinition;
59
+ type RenameRelationOpts = FieldNameOverrides & Pick<ColumnDefinition, "comment">;
60
+ declare function renameRelation(b: MigrationBuilder, tableName: string, columnName: string, opts: RenameRelationOpts): void;
61
+ declare function commentData(data: any, comment?: string | null): string;
62
+ declare function enumArrayColumn(enumTable: string, opts?: Pick<ColumnDefinition, "notNull">): ColumnDefinition;
63
+ type ManyToManyColumn = {
64
+ /** The target table for this m2m table, i.e. for a `books_to_tags`, this might be `books`. */
65
+ table: string;
66
+ /** The column name within the m2m table, i.e. for a `books_to_tags`, this might be `book_id`. */
67
+ column?: string;
68
+ /**
69
+ * The name of the collection that *points to* these rows, i.e. for `books_to_tags` and the `book_id`
70
+ * column, `collectionName=taggedBooks` b/c `t1.taggedBooks` does a `SELECT book_id WHERE tag_id=t:1`.
71
+ *
72
+ * For self-referential m2m tables, i.e. `author_to_mentors`, these names can be confusing, i.e.:
73
+ *
74
+ * ```
75
+ * createManyToManyTable(
76
+ * b,
77
+ * "author_to_mentors",
78
+ * // column=mentor_id, collectionName=mentors ==> `a.mentors` does `select mentor_id WHERE mentee_id=a:1`
79
+ * { table: "authors", column: "mentor_id", collectionName: "mentors" },
80
+ * // column=mentee_id, collectionName=mentees ==> `a.mentees` does `select mentee_id WHERE mentor_id=a:1`
81
+ * { table: "authors", column: "mentee_id", collectionName: "mentees" },
82
+ * );
83
+ * ````
84
+ */
85
+ collectionName?: string;
86
+ };
87
+ /** Creates a many-to-many table between `table1` and `table2` with our conventions. */
88
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, table1: string, table2: string, options?: TableOptions & DropOptions): void;
89
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, column1: ManyToManyColumn, column2: ManyToManyColumn, options?: TableOptions & DropOptions): void;
90
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, table1: string, column2: ManyToManyColumn, options?: TableOptions & DropOptions): void;
91
+ declare function createManyToManyTable(b: MigrationBuilder, tableName: string, column1: ManyToManyColumn, table2: string, options?: TableOptions & DropOptions): void;
92
+ /** Adds columns + auto-indexes any foreign keys. */
93
+ declare function addColumns(b: MigrationBuilder, tableName: string, columns: ColumnDefinitions): void;
94
+ declare function fail(message?: string): never;
95
+ //#endregion
96
+ export { FieldNameOverrides, RenameRelationOpts, addColumns, addEnumValue, commentData, createCreatedAtFunction, createEntityTable, createEnumTable, createManyToManyTable, createSubTable, createTriggers, createUnnestArraysFunction, createUpdatedAtFunction, enumArrayColumn, fail, foreignKey, renameRelation, unnest_arrays, updateEnumValue };
97
+ //# sourceMappingURL=utils.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.mts","names":[],"sources":["../src/utils.ts"],"mappings":";;;;;;;;iBAkBgB,kBAAkB,GAAG,kBAAkB,mBAAmB,SAAS;;;;;;;iBAyBnE,eACd,GAAG,kBACH,uBACA,sBACA,SAAS;iBAsBK,gBAAgB,GAAG,kBAAkB,mBAAmB,QAAQ;iBAShE,aAAa,GAAG,kBAAkB,mBAAmB;iBAMrD,gBACd,GAAG,kBACH,mBACA,sBACA;;iBAcc,eAAe,GAAG,kBAAkB;iBAepC,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoC9B;iBAmBG,2BAA2B,GAAG;iBAI9B,wBAAwB,GAAG;KAS/B;EACV;EACA;;KAEG,iBAAiB,QAAQ,oBAAoB,SAAS,KAAK,gCAAgC;iBAChF,WAAW,oBAAoB,MAAM,iBAAiB;KAU1D,qBAAqB,qBAAqB,KAAK;iBAC3C,eAAe,GAAG,kBAAkB,mBAAmB,oBAAoB,MAAM;iBAIjF,YAAY,WAAW;iBAkBvB,gBAAgB,mBAAmB,OAAO,KAAK,+BAA+B;KAazF;;EAEH;;EAEA;;;;;;;;;;;;;;;;;;EAkBA;;;iBAac,sBACd,GAAG,kBACH,mBACA,gBACA,gBACA,UAAU,eAAe;iBAEX,sBACd,GAAG,kBACH,mBACA,SAAS,kBACT,SAAS,kBACT,UAAU,eAAe;iBAEX,sBACd,GAAG,kBACH,mBACA,gBACA,SAAS,kBACT,UAAU,eAAe;iBAEX,sBACd,GAAG,kBACH,mBACA,SAAS,kBACT,gBACA,UAAU,eAAe;;iBA0BX,WAAW,GAAG,kBAAkB,mBAAmB,SAAS;iBAS5D,KAAK"}
package/build/utils.js CHANGED
@@ -1,142 +1,132 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.unnest_arrays = void 0;
4
- exports.createEntityTable = createEntityTable;
5
- exports.createSubTable = createSubTable;
6
- exports.createEnumTable = createEnumTable;
7
- exports.addEnumValue = addEnumValue;
8
- exports.updateEnumValue = updateEnumValue;
9
- exports.createTriggers = createTriggers;
10
- exports.createUpdatedAtFunction = createUpdatedAtFunction;
11
- exports.createUnnestArraysFunction = createUnnestArraysFunction;
12
- exports.createCreatedAtFunction = createCreatedAtFunction;
13
- exports.foreignKey = foreignKey;
14
- exports.renameRelation = renameRelation;
15
- exports.commentData = commentData;
16
- exports.enumArrayColumn = enumArrayColumn;
17
- exports.createManyToManyTable = createManyToManyTable;
18
- exports.addColumns = addColumns;
19
- exports.fail = fail;
20
- const node_pg_migrate_1 = require("node-pg-migrate");
21
- const pluralize_1 = require("pluralize");
1
+ import { PgLiteral } from "node-pg-migrate";
2
+ import pluralize from "pluralize";
3
+ //#region src/utils.ts
4
+ const { singular } = pluralize;
22
5
  /**
23
- * Creates an entity table with our conventions.
24
- *
25
- * Specifically an `id` auto-increment column (via a sequence) and `created_at` and `updated_at`
26
- * columns.
27
- */
6
+ * Creates an entity table with our conventions.
7
+ *
8
+ * Specifically an `id` auto-increment column (via a sequence) and `created_at` and `updated_at`
9
+ * columns.
10
+ */
28
11
  function createEntityTable(b, tableName, columns) {
29
- b.createTable(tableName, {
30
- id: "id",
31
- ...columns,
32
- created_at: { type: "timestamptz", notNull: true },
33
- updated_at: { type: "timestamptz", notNull: true },
34
- });
35
- // Postgres doesn't automatically index foreign keys, so any column def points at
36
- // another table, assume we'll be doing a lot of lookups on this column and should fk it.
37
- Object.entries(columns).forEach(([name, def]) => {
38
- if (typeof def === "object" && def.references) {
39
- b.addIndex(tableName, [name], { method: "btree" });
40
- }
41
- });
42
- createTriggers(b, tableName);
12
+ b.createTable(tableName, {
13
+ id: "id",
14
+ ...columns,
15
+ created_at: {
16
+ type: "timestamptz",
17
+ notNull: true
18
+ },
19
+ updated_at: {
20
+ type: "timestamptz",
21
+ notNull: true
22
+ }
23
+ });
24
+ Object.entries(columns).forEach(([name, def]) => {
25
+ if (typeof def === "object" && def.references) b.addIndex(tableName, [name], { method: "btree" });
26
+ });
27
+ createTriggers(b, tableName);
43
28
  }
44
29
  /**
45
- * Creates a subtype table using class-per-table inheritance.
46
- *
47
- * The subtable will use the base table's id as its identity, and when loading rows of the base
48
- * type, Joist will automatically stitch together rows across each table into a single instance.
49
- */
30
+ * Creates a subtype table using class-per-table inheritance.
31
+ *
32
+ * The subtable will use the base table's id as its identity, and when loading rows of the base
33
+ * type, Joist will automatically stitch together rows across each table into a single instance.
34
+ */
50
35
  function createSubTable(b, baseTableName, subTableName, columns) {
51
- b.createTable(subTableName, {
52
- id: {
53
- type: "int",
54
- references: `${baseTableName}`,
55
- primaryKey: true,
56
- deferrable: true,
57
- deferred: true,
58
- onDelete: "CASCADE",
59
- },
60
- ...columns,
61
- });
62
- // Postgres doesn't automatically index foreign keys, so any column def points at
63
- // another table, assume we'll be doing a lot of lookups on this column and should fk it.
64
- Object.entries(columns).forEach(([name, def]) => {
65
- if (typeof def === "object" && def.references) {
66
- b.addIndex(subTableName, [name], { method: "btree" });
67
- }
68
- });
36
+ b.createTable(subTableName, {
37
+ id: {
38
+ type: "int",
39
+ references: `${baseTableName}`,
40
+ primaryKey: true,
41
+ deferrable: true,
42
+ deferred: true,
43
+ onDelete: "CASCADE"
44
+ },
45
+ ...columns
46
+ });
47
+ Object.entries(columns).forEach(([name, def]) => {
48
+ if (typeof def === "object" && def.references) b.addIndex(subTableName, [name], { method: "btree" });
49
+ });
69
50
  }
70
51
  function createEnumTable(b, tableName, values) {
71
- b.createTable(tableName, {
72
- id: "id",
73
- code: { type: "text", notNull: true },
74
- name: { type: "text", notNull: true },
75
- });
76
- b.addConstraint(tableName, `${tableName}_unique_enum_code_constraint`, "UNIQUE (code)");
77
- values.forEach((value) => addEnumValue(b, tableName, value));
52
+ b.createTable(tableName, {
53
+ id: "id",
54
+ code: {
55
+ type: "text",
56
+ notNull: true
57
+ },
58
+ name: {
59
+ type: "text",
60
+ notNull: true
61
+ }
62
+ });
63
+ b.addConstraint(tableName, `${tableName}_unique_enum_code_constraint`, "UNIQUE (code)");
64
+ values.forEach((value) => addEnumValue(b, tableName, value));
78
65
  }
79
66
  function addEnumValue(b, tableName, value) {
80
- const [code, name] = value;
81
- validateEnumCode(code);
82
- b.sql(`INSERT INTO ${tableName} (code, name) VALUES ('${code}', '${name.replace("'", "''")}');`);
67
+ const [code, name] = value;
68
+ validateEnumCode(code);
69
+ b.sql(`INSERT INTO ${tableName} (code, name) VALUES ('${code}', '${name.replace("'", "''")}');`);
83
70
  }
84
71
  function updateEnumValue(b, tableName, previousCode, value) {
85
- const [code, name] = value;
86
- validateEnumCode(code);
87
- b.sql(`UPDATE ${tableName} SET code ='${code}', name = '${name.replace("'", "''")}' WHERE code = '${previousCode}';`);
72
+ const [code, name] = value;
73
+ validateEnumCode(code);
74
+ b.sql(`UPDATE ${tableName} SET code ='${code}', name = '${name.replace("'", "''")}' WHERE code = '${previousCode}';`);
88
75
  }
89
76
  function validateEnumCode(code) {
90
- const codeRegex = /^[A-Z0-9_]+$/;
91
- if (!codeRegex.test(code))
92
- throw `ERROR: Invalid enum code specified: ${code}. Codes must match the regex: ${codeRegex}`;
77
+ const codeRegex = /^[A-Z0-9_]+$/;
78
+ if (!codeRegex.test(code)) throw `ERROR: Invalid enum code specified: ${code}. Codes must match the regex: ${codeRegex}`;
93
79
  }
94
80
  /** Makes a trigger to update the `updated_at` column. */
95
81
  function createTriggers(b, tableName) {
96
- b.createTrigger(tableName, `${tableName}_created_at`, {
97
- when: "BEFORE",
98
- operation: "INSERT",
99
- level: "ROW",
100
- function: "trigger_maybe_set_created_at",
101
- });
102
- b.createTrigger(tableName, `${tableName}_updated_at`, {
103
- when: "BEFORE",
104
- operation: "UPDATE",
105
- level: "ROW",
106
- function: "trigger_maybe_set_updated_at",
107
- });
82
+ b.createTrigger(tableName, `${tableName}_created_at`, {
83
+ when: "BEFORE",
84
+ operation: "INSERT",
85
+ level: "ROW",
86
+ function: "trigger_maybe_set_created_at"
87
+ });
88
+ b.createTrigger(tableName, `${tableName}_updated_at`, {
89
+ when: "BEFORE",
90
+ operation: "UPDATE",
91
+ level: "ROW",
92
+ function: "trigger_maybe_set_updated_at"
93
+ });
108
94
  }
109
95
  function createUpdatedAtFunction(b) {
110
- b.createFunction("trigger_maybe_set_updated_at", [], { replace: true, language: "plpgsql", returns: "TRIGGER" }, "BEGIN IF NEW.updated_at = OLD.updated_at THEN NEW.updated_at = NOW(); END IF; RETURN NEW; END;");
96
+ b.createFunction("trigger_maybe_set_updated_at", [], {
97
+ replace: true,
98
+ language: "plpgsql",
99
+ returns: "TRIGGER"
100
+ }, "BEGIN IF NEW.updated_at = OLD.updated_at THEN NEW.updated_at = NOW(); END IF; RETURN NEW; END;");
111
101
  }
112
102
  /**
113
- * Unnests a 2d array like `[[a, b], [c, d]]` into two rows of `[a, b]` and `[c, d]`.
114
- *
115
- * This custom function is a key part of Joist's bulk INSERT/UPDATE that sends "all firstNames as
116
- * a single param", because it lets us support array columns as a single parameter (i.e. an array
117
- * of arrays).
118
- *
119
- * Postgres's built-in `unnest` is too aggressive when flattening, and will unnest the 2d array
120
- * of `[[a, b], [c, d]]` into four rows of `a`, `b`, `c`, and `d`, which is not what we want.
121
- *
122
- * Additionally, Postgres does not support "jagged arrays", i.e. 2D arrays where some
123
- * sub-arrays are longer than others, e.g. `[[a, b], [c, d, e]]`. Instead, it treats arrays
124
- * more as the mathematical concept of a matrix, than "a list of lists".
125
- *
126
- * To work around this, our PostgresDriver auto-pads all 2D arrays to the same length, by
127
- * appending `NULL`, and then `unnest_arrays` prunes all `NULL`s from the resulting 1D arrays.
128
- * See `fillArrayWithNulls`.
129
- *
130
- *
131
- * This means we purposefully do not/cannot support `NULL` values _within_ array columns,
132
- * i.e. `nick_names=[a, NULL, b]` (without some additional heuristics).
133
- *
134
- * That said, we do need to support nullable array columns, i.e. `nick_names=NULL`, which we
135
- * detect by prefixing an additional marker element to each sub-array that indicates whether the
136
- * array column is null or not, and then conditionally enabling this with the `nullable=true`
137
- * param to `unnest_arrays`.
138
- */
139
- exports.unnest_arrays = `
103
+ * Unnests a 2d array like `[[a, b], [c, d]]` into two rows of `[a, b]` and `[c, d]`.
104
+ *
105
+ * This custom function is a key part of Joist's bulk INSERT/UPDATE that sends "all firstNames as
106
+ * a single param", because it lets us support array columns as a single parameter (i.e. an array
107
+ * of arrays).
108
+ *
109
+ * Postgres's built-in `unnest` is too aggressive when flattening, and will unnest the 2d array
110
+ * of `[[a, b], [c, d]]` into four rows of `a`, `b`, `c`, and `d`, which is not what we want.
111
+ *
112
+ * Additionally, Postgres does not support "jagged arrays", i.e. 2D arrays where some
113
+ * sub-arrays are longer than others, e.g. `[[a, b], [c, d, e]]`. Instead, it treats arrays
114
+ * more as the mathematical concept of a matrix, than "a list of lists".
115
+ *
116
+ * To work around this, our PostgresDriver auto-pads all 2D arrays to the same length, by
117
+ * appending `NULL`, and then `unnest_arrays` prunes all `NULL`s from the resulting 1D arrays.
118
+ * See `fillArrayWithNulls`.
119
+ *
120
+ *
121
+ * This means we purposefully do not/cannot support `NULL` values _within_ array columns,
122
+ * i.e. `nick_names=[a, NULL, b]` (without some additional heuristics).
123
+ *
124
+ * That said, we do need to support nullable array columns, i.e. `nick_names=NULL`, which we
125
+ * detect by prefixing an additional marker element to each sub-array that indicates whether the
126
+ * array column is null or not, and then conditionally enabling this with the `nullable=true`
127
+ * param to `unnest_arrays`.
128
+ */
129
+ const unnest_arrays = `
140
130
  CREATE OR REPLACE FUNCTION unnest_arrays(arr ANYARRAY, nullable BOOLEAN = false, OUT a ANYARRAY)
141
131
  RETURNS SETOF ANYARRAY
142
132
  LANGUAGE plpgsql IMMUTABLE STRICT AS
@@ -155,82 +145,109 @@ END
155
145
  $func$;
156
146
  `;
157
147
  function createUnnestArraysFunction(b) {
158
- b.sql(exports.unnest_arrays);
148
+ b.sql(unnest_arrays);
159
149
  }
160
150
  function createCreatedAtFunction(b) {
161
- b.createFunction("trigger_maybe_set_created_at", [], { replace: true, language: "plpgsql", returns: "TRIGGER" }, "BEGIN IF NEW.created_at IS NULL THEN NEW.created_at = NOW(); END IF; IF NEW.updated_at IS NULL THEN NEW.updated_at = NOW(); END IF; RETURN NEW; END;");
151
+ b.createFunction("trigger_maybe_set_created_at", [], {
152
+ replace: true,
153
+ language: "plpgsql",
154
+ returns: "TRIGGER"
155
+ }, "BEGIN IF NEW.created_at IS NULL THEN NEW.created_at = NOW(); END IF; IF NEW.updated_at IS NULL THEN NEW.updated_at = NOW(); END IF; RETURN NEW; END;");
162
156
  }
163
157
  function foreignKey(otherTable, opts) {
164
- return {
165
- type: "integer",
166
- references: otherTable,
167
- deferrable: true,
168
- deferred: true,
169
- ...foreignKeyOptsWithMaybeComment(opts),
170
- };
158
+ return {
159
+ type: "integer",
160
+ references: otherTable,
161
+ deferrable: true,
162
+ deferred: true,
163
+ ...foreignKeyOptsWithMaybeComment(opts)
164
+ };
171
165
  }
172
166
  function renameRelation(b, tableName, columnName, opts) {
173
- b.alterColumn(tableName, columnName, foreignKeyOptsWithMaybeComment(opts));
167
+ b.alterColumn(tableName, columnName, foreignKeyOptsWithMaybeComment(opts));
174
168
  }
175
169
  function commentData(data, comment) {
176
- return `${comment ?? ""}[pg-structure]${JSON.stringify(data)}[/pg-structure]`;
170
+ return `${comment ?? ""}[pg-structure]${JSON.stringify(data)}[/pg-structure]`;
177
171
  }
178
172
  function foreignKeyOptsWithMaybeComment(opts) {
179
- let { comment, fieldName, otherFieldName, ...rest } = opts;
180
- if (fieldName || otherFieldName) {
181
- const overrides = { fieldName, otherFieldName };
182
- return { ...rest, comment: commentData(overrides, comment) };
183
- }
184
- else if (comment) {
185
- return { ...rest, comment };
186
- }
187
- else {
188
- return rest;
189
- }
173
+ let { comment, fieldName, otherFieldName, ...rest } = opts;
174
+ if (fieldName || otherFieldName) {
175
+ const overrides = {
176
+ fieldName,
177
+ otherFieldName
178
+ };
179
+ return {
180
+ ...rest,
181
+ comment: commentData(overrides, comment)
182
+ };
183
+ } else if (comment) return {
184
+ ...rest,
185
+ comment
186
+ };
187
+ else return rest;
190
188
  }
191
189
  function enumArrayColumn(enumTable, opts) {
192
- // We use `notNull: false` to facilitate adding to tables with existing rows,
193
- // but note that we coalesce null to `[]` anyway in the serde logic, so it
194
- // doesn't really matter to application logic whether it's notNull or not.
195
- return {
196
- type: "integer[]",
197
- comment: `enum=${enumTable}`,
198
- notNull: false,
199
- default: node_pg_migrate_1.PgLiteral.create("array[]::integer[]"),
200
- ...opts,
201
- };
190
+ return {
191
+ type: "integer[]",
192
+ comment: `enum=${enumTable}`,
193
+ notNull: false,
194
+ default: PgLiteral.create("array[]::integer[]"),
195
+ ...opts
196
+ };
202
197
  }
203
198
  function maybeTableOrColumn(maybeTableOrColumn) {
204
- if (typeof maybeTableOrColumn === "string") {
205
- return [maybeTableOrColumn, `${(0, pluralize_1.singular)(maybeTableOrColumn)}_id`, undefined];
206
- }
207
- else {
208
- const { table, column, collectionName } = maybeTableOrColumn;
209
- return [table, column ?? `${(0, pluralize_1.singular)(table)}_id`, collectionName];
210
- }
199
+ if (typeof maybeTableOrColumn === "string") return [
200
+ maybeTableOrColumn,
201
+ `${singular(maybeTableOrColumn)}_id`,
202
+ void 0
203
+ ];
204
+ else {
205
+ const { table, column, collectionName } = maybeTableOrColumn;
206
+ return [
207
+ table,
208
+ column ?? `${singular(table)}_id`,
209
+ collectionName
210
+ ];
211
+ }
211
212
  }
212
213
  function createManyToManyTable(b, tableName, tableOrColumn1, tableOrColumn2, options) {
213
- const [table1, column1, otherFieldName1] = maybeTableOrColumn(tableOrColumn1);
214
- const [table2, column2, otherFieldName2] = maybeTableOrColumn(tableOrColumn2);
215
- b.createTable(tableName, {
216
- id: "id",
217
- [column1]: foreignKey(table1, { notNull: true, onDelete: "CASCADE", otherFieldName: otherFieldName1 }),
218
- [column2]: foreignKey(table2, { notNull: true, onDelete: "CASCADE", otherFieldName: otherFieldName2 }),
219
- created_at: { type: "timestamptz", notNull: true, default: b.func("NOW()") },
220
- }, options);
221
- b.createIndex(tableName, [column1, column2], { unique: true, ifNotExists: options?.ifNotExists });
222
- b.createIndex(tableName, [column2]); // Improves lookup performance when querying just column2
214
+ const [table1, column1, otherFieldName1] = maybeTableOrColumn(tableOrColumn1);
215
+ const [table2, column2, otherFieldName2] = maybeTableOrColumn(tableOrColumn2);
216
+ b.createTable(tableName, {
217
+ id: "id",
218
+ [column1]: foreignKey(table1, {
219
+ notNull: true,
220
+ onDelete: "CASCADE",
221
+ otherFieldName: otherFieldName1
222
+ }),
223
+ [column2]: foreignKey(table2, {
224
+ notNull: true,
225
+ onDelete: "CASCADE",
226
+ otherFieldName: otherFieldName2
227
+ }),
228
+ created_at: {
229
+ type: "timestamptz",
230
+ notNull: true,
231
+ default: b.func("NOW()")
232
+ }
233
+ }, options);
234
+ b.createIndex(tableName, [column1, column2], {
235
+ unique: true,
236
+ ifNotExists: options?.ifNotExists
237
+ });
238
+ b.createIndex(tableName, [column2]);
223
239
  }
224
240
  /** Adds columns + auto-indexes any foreign keys. */
225
241
  function addColumns(b, tableName, columns) {
226
- b.addColumns(tableName, columns);
227
- Object.entries(columns).forEach(([name, def]) => {
228
- if (typeof def === "object" && def.references) {
229
- b.addIndex(tableName, [name], { method: "btree" });
230
- }
231
- });
242
+ b.addColumns(tableName, columns);
243
+ Object.entries(columns).forEach(([name, def]) => {
244
+ if (typeof def === "object" && def.references) b.addIndex(tableName, [name], { method: "btree" });
245
+ });
232
246
  }
233
247
  function fail(message) {
234
- throw new Error(message || "Failed");
248
+ throw new Error(message || "Failed");
235
249
  }
250
+ //#endregion
251
+ export { addColumns, addEnumValue, commentData, createCreatedAtFunction, createEntityTable, createEnumTable, createManyToManyTable, createSubTable, createTriggers, createUnnestArraysFunction, createUpdatedAtFunction, enumArrayColumn, fail, foreignKey, renameRelation, unnest_arrays, updateEnumValue };
252
+
236
253
  //# sourceMappingURL=utils.js.map