@casekit/orm-migrate 0.0.1-release-candidate

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.
Files changed (55) hide show
  1. package/build/drop.d.ts +2 -0
  2. package/build/drop.js +12 -0
  3. package/build/index.d.ts +7 -0
  4. package/build/index.js +8 -0
  5. package/build/pull/getColumns.d.ts +34 -0
  6. package/build/pull/getColumns.js +83 -0
  7. package/build/pull/getColumns.test.d.ts +1 -0
  8. package/build/pull/getColumns.test.js +320 -0
  9. package/build/pull/getForeignKeys.d.ts +18 -0
  10. package/build/pull/getForeignKeys.js +72 -0
  11. package/build/pull/getForeignKeys.test.d.ts +1 -0
  12. package/build/pull/getForeignKeys.test.js +86 -0
  13. package/build/pull/getPrimaryKeys.d.ts +14 -0
  14. package/build/pull/getPrimaryKeys.js +30 -0
  15. package/build/pull/getPrimaryKeys.test.d.ts +1 -0
  16. package/build/pull/getPrimaryKeys.test.js +87 -0
  17. package/build/pull/getUniqueConstraints.d.ts +14 -0
  18. package/build/pull/getUniqueConstraints.js +27 -0
  19. package/build/pull/getUniqueConstraints.test.d.ts +1 -0
  20. package/build/pull/getUniqueConstraints.test.js +99 -0
  21. package/build/pull/index.d.ts +4 -0
  22. package/build/pull/index.js +1 -0
  23. package/build/pull/pullDefault.d.ts +6 -0
  24. package/build/pull/pullDefault.js +44 -0
  25. package/build/pull/pullDefault.test.d.ts +1 -0
  26. package/build/pull/pullDefault.test.js +82 -0
  27. package/build/pull.d.ts +14 -0
  28. package/build/pull.js +56 -0
  29. package/build/push/arrayToSqlArray.d.ts +1 -0
  30. package/build/push/arrayToSqlArray.js +6 -0
  31. package/build/push/arrayToSqlArray.test.d.ts +1 -0
  32. package/build/push/arrayToSqlArray.test.js +29 -0
  33. package/build/push/createExtensionSql.d.ts +2 -0
  34. package/build/push/createExtensionSql.js +4 -0
  35. package/build/push/createExtensionSql.test.d.ts +1 -0
  36. package/build/push/createExtensionSql.test.js +20 -0
  37. package/build/push/createForeignKeyConstraintSql.d.ts +3 -0
  38. package/build/push/createForeignKeyConstraintSql.js +17 -0
  39. package/build/push/createForeignKeyConstraintSql.test.d.ts +1 -0
  40. package/build/push/createForeignKeyConstraintSql.test.js +142 -0
  41. package/build/push/createSchemaSql.d.ts +1 -0
  42. package/build/push/createSchemaSql.js +7 -0
  43. package/build/push/createSchemaSql.test.d.ts +1 -0
  44. package/build/push/createSchemaSql.test.js +26 -0
  45. package/build/push/createTableSql.d.ts +3 -0
  46. package/build/push/createTableSql.js +38 -0
  47. package/build/push/createTableSql.test.d.ts +1 -0
  48. package/build/push/createTableSql.test.js +110 -0
  49. package/build/push/createUniqueConstraintSql.d.ts +2 -0
  50. package/build/push/createUniqueConstraintSql.js +17 -0
  51. package/build/push/createUniqueConstraintSql.test.d.ts +1 -0
  52. package/build/push/createUniqueConstraintSql.test.js +105 -0
  53. package/build/push.d.ts +2 -0
  54. package/build/push.js +38 -0
  55. package/package.json +59 -0
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { sql } from "@casekit/orm";
3
+ export const PrimaryKeySchema = z.object({
4
+ schema: z.string(),
5
+ table: z.string(),
6
+ constraintName: z.string(),
7
+ columns: z.array(z.string()),
8
+ });
9
+ export const getPrimaryKeys = (schemas) => sql(PrimaryKeySchema) `
10
+ SELECT
11
+ c.constraint_schema AS "schema",
12
+ c.table_name AS "table",
13
+ c.constraint_name AS "constraintName",
14
+ array_agg(cc.column_name::text ORDER BY cc.ordinal_position) AS "columns"
15
+ FROM
16
+ information_schema.table_constraints c
17
+ JOIN information_schema.key_column_usage cc ON c.constraint_name = cc.constraint_name
18
+ AND c.constraint_schema = cc.constraint_schema
19
+ AND c.table_name = cc.table_name
20
+ WHERE
21
+ c.constraint_type = 'PRIMARY KEY'
22
+ AND c.constraint_schema IN (${schemas})
23
+ GROUP BY
24
+ c.constraint_schema,
25
+ c.table_name,
26
+ c.constraint_name
27
+ ORDER BY
28
+ c.constraint_schema,
29
+ c.table_name
30
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,87 @@
1
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
2
+ import { orm, sql } from "@casekit/orm";
3
+ import { getPrimaryKeys } from "./getPrimaryKeys.js";
4
+ describe("getPrimaryKeys", () => {
5
+ const db = orm({
6
+ schema: "migrate-get-primary-keys-test",
7
+ models: {},
8
+ });
9
+ beforeAll(async () => {
10
+ await db.connect();
11
+ });
12
+ afterAll(async () => {
13
+ await db.close();
14
+ });
15
+ beforeEach(async () => {
16
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-primary-keys-test" CASCADE;`;
17
+ await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-primary-keys-test";`;
18
+ });
19
+ afterEach(async () => {
20
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-primary-keys-test" CASCADE;`;
21
+ });
22
+ test("with an empty database it returns no primary keys", async () => {
23
+ const statement = getPrimaryKeys(["migrate-get-primary-keys-test"]);
24
+ const result = await db.query(statement);
25
+ expect(result).toEqual([]);
26
+ });
27
+ test("returns single column primary key", async () => {
28
+ await db.transact(async (db) => {
29
+ await db.query(sql `
30
+ CREATE TABLE "migrate-get-primary-keys-test"."users" (
31
+ "id" SERIAL PRIMARY KEY,
32
+ "name" TEXT NOT NULL
33
+ );
34
+ `);
35
+ const result = await db.query(getPrimaryKeys(["migrate-get-primary-keys-test"]));
36
+ expect(result).toEqual([
37
+ expect.objectContaining({
38
+ schema: "migrate-get-primary-keys-test",
39
+ table: "users",
40
+ constraintName: "users_pkey",
41
+ columns: ["id"],
42
+ }),
43
+ ]);
44
+ }, { rollback: true });
45
+ });
46
+ test("returns composite primary key with correct order", async () => {
47
+ await db.transact(async (db) => {
48
+ await db.query(sql `
49
+ CREATE TABLE "migrate-get-primary-keys-test"."user_roles" (
50
+ "user_id" INTEGER NOT NULL,
51
+ "role_id" INTEGER NOT NULL,
52
+ "granted_at" TIMESTAMP DEFAULT NOW(),
53
+ PRIMARY KEY ("user_id", "role_id")
54
+ );
55
+ `);
56
+ const result = await db.query(getPrimaryKeys(["migrate-get-primary-keys-test"]));
57
+ expect(result).toEqual([
58
+ expect.objectContaining({
59
+ schema: "migrate-get-primary-keys-test",
60
+ table: "user_roles",
61
+ constraintName: "user_roles_pkey",
62
+ columns: ["user_id", "role_id"],
63
+ }),
64
+ ]);
65
+ }, { rollback: true });
66
+ });
67
+ test("returns named primary key constraint", async () => {
68
+ await db.transact(async (db) => {
69
+ await db.query(sql `
70
+ CREATE TABLE "migrate-get-primary-keys-test"."products" (
71
+ "id" UUID NOT NULL,
72
+ "name" TEXT NOT NULL,
73
+ CONSTRAINT "pk_products_id" PRIMARY KEY ("id")
74
+ );
75
+ `);
76
+ const result = await db.query(getPrimaryKeys(["migrate-get-primary-keys-test"]));
77
+ expect(result).toEqual([
78
+ expect.objectContaining({
79
+ schema: "migrate-get-primary-keys-test",
80
+ table: "products",
81
+ constraintName: "pk_products_id",
82
+ columns: ["id"],
83
+ }),
84
+ ]);
85
+ }, { rollback: true });
86
+ });
87
+ });
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ export declare const UniqueConstraintSchema: z.ZodObject<{
3
+ schema: z.ZodString;
4
+ table: z.ZodString;
5
+ name: z.ZodString;
6
+ definition: z.ZodString;
7
+ }, z.core.$strip>;
8
+ export type UniqueConstraint = z.infer<typeof UniqueConstraintSchema>;
9
+ export declare const getUniqueConstraints: (schemas: string[]) => import("@casekit/orm").SQLStatement<{
10
+ schema: string;
11
+ table: string;
12
+ name: string;
13
+ definition: string;
14
+ }>;
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ import { sql } from "@casekit/orm";
3
+ export const UniqueConstraintSchema = z.object({
4
+ schema: z.string(),
5
+ table: z.string(),
6
+ name: z.string(),
7
+ definition: z.string(),
8
+ });
9
+ export const getUniqueConstraints = (schemas) => sql(UniqueConstraintSchema) `
10
+ SELECT
11
+ i.schemaname AS "schema",
12
+ i.tablename AS "table",
13
+ i.indexname AS "name",
14
+ i.indexdef AS "definition"
15
+ FROM
16
+ pg_indexes i
17
+ LEFT JOIN pg_constraint c ON i.indexname = c.conname
18
+ AND c.contype IN ('p', 'x')
19
+ WHERE
20
+ i.schemaname IN (${schemas})
21
+ AND c.conname IS NULL
22
+ AND i.indexdef LIKE 'CREATE UNIQUE INDEX%'
23
+ ORDER BY
24
+ i.schemaname,
25
+ i.tablename,
26
+ i.indexname
27
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
2
+ import { orm, sql } from "@casekit/orm";
3
+ import { getUniqueConstraints } from "./getUniqueConstraints.js";
4
+ describe("getUniqueConstraints", () => {
5
+ const db = orm({
6
+ schema: "migrate-get-unique-constraints-test",
7
+ models: {},
8
+ });
9
+ beforeAll(async () => {
10
+ await db.connect();
11
+ });
12
+ afterAll(async () => {
13
+ await db.close();
14
+ });
15
+ beforeEach(async () => {
16
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-unique-constraints-test" CASCADE;`;
17
+ await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-unique-constraints-test";`;
18
+ });
19
+ afterEach(async () => {
20
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-unique-constraints-test" CASCADE;`;
21
+ });
22
+ test("with an empty database it returns no unique constraints", async () => {
23
+ const statement = getUniqueConstraints([
24
+ "migrate-get-unique-constraints-test",
25
+ ]);
26
+ const result = await db.query(statement);
27
+ expect(result).toEqual([]);
28
+ });
29
+ test("returns unique index constraints", async () => {
30
+ await db.transact(async (db) => {
31
+ await db.query(sql `
32
+ CREATE TABLE "migrate-get-unique-constraints-test"."users" (
33
+ "id" SERIAL PRIMARY KEY,
34
+ "email" TEXT NOT NULL
35
+ );
36
+ `);
37
+ await db.query(sql `
38
+ CREATE UNIQUE INDEX "idx_users_email" ON "migrate-get-unique-constraints-test"."users" ("email");
39
+ `);
40
+ const result = await db.query(getUniqueConstraints([
41
+ "migrate-get-unique-constraints-test",
42
+ ]));
43
+ expect(result).toEqual([
44
+ expect.objectContaining({
45
+ schema: "migrate-get-unique-constraints-test",
46
+ table: "users",
47
+ name: "idx_users_email",
48
+ definition: expect.stringContaining("CREATE UNIQUE INDEX idx_users_email"),
49
+ }),
50
+ ]);
51
+ }, { rollback: true });
52
+ });
53
+ test("returns composite unique index constraints", async () => {
54
+ await db.transact(async (db) => {
55
+ await db.query(sql `
56
+ CREATE TABLE "migrate-get-unique-constraints-test"."products" (
57
+ "id" SERIAL PRIMARY KEY,
58
+ "name" TEXT NOT NULL,
59
+ "category" TEXT NOT NULL
60
+ );
61
+ `);
62
+ await db.query(sql `
63
+ CREATE UNIQUE INDEX "idx_products_name_category" ON "migrate-get-unique-constraints-test"."products" ("name", "category");
64
+ `);
65
+ const result = await db.query(getUniqueConstraints([
66
+ "migrate-get-unique-constraints-test",
67
+ ]));
68
+ expect(result).toEqual([
69
+ expect.objectContaining({
70
+ schema: "migrate-get-unique-constraints-test",
71
+ table: "products",
72
+ name: "idx_products_name_category",
73
+ definition: expect.stringContaining("CREATE UNIQUE INDEX idx_products_name_category"),
74
+ }),
75
+ ]);
76
+ }, { rollback: true });
77
+ });
78
+ test("excludes primary key constraints", async () => {
79
+ await db.transact(async (db) => {
80
+ await db.query(sql `
81
+ CREATE TABLE "migrate-get-unique-constraints-test"."users" (
82
+ "id" SERIAL PRIMARY KEY,
83
+ "email" TEXT UNIQUE
84
+ );
85
+ `);
86
+ const result = await db.query(getUniqueConstraints([
87
+ "migrate-get-unique-constraints-test",
88
+ ]));
89
+ // Should only return the unique constraint on email, not the primary key
90
+ expect(result).toHaveLength(1);
91
+ expect(result[0]).toEqual(expect.objectContaining({
92
+ schema: "migrate-get-unique-constraints-test",
93
+ table: "users",
94
+ definition: expect.stringContaining("email"),
95
+ }));
96
+ expect(result[0].definition).not.toContain("id");
97
+ }, { rollback: true });
98
+ });
99
+ });
@@ -0,0 +1,4 @@
1
+ export type { Column } from "./getColumns.js";
2
+ export type { ForeignKey } from "./getForeignKeys.js";
3
+ export type { PrimaryKey } from "./getPrimaryKeys.js";
4
+ export type { UniqueConstraint } from "./getUniqueConstraints.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Normalizes PostgreSQL default values to their canonical forms
3
+ * This is the inverse of renderDefault - it takes raw PostgreSQL defaults
4
+ * and converts them back to standard representations.
5
+ */
6
+ export declare const pullDefault: (defaultValue: string | null) => string | null;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Normalizes PostgreSQL default values to their canonical forms
3
+ * This is the inverse of renderDefault - it takes raw PostgreSQL defaults
4
+ * and converts them back to standard representations.
5
+ */
6
+ export const pullDefault = (defaultValue) => {
7
+ if (defaultValue === null) {
8
+ return null;
9
+ }
10
+ // Only convert when the default is literally "now()" - not timestamp literals
11
+ if (defaultValue === "now()") {
12
+ return "now()";
13
+ }
14
+ // Handle other common SQL functions that should remain as function calls
15
+ if (/^(gen_random_uuid|uuid_generate_v\d+|current_timestamp|current_date|current_time)\(\)$/.test(defaultValue)) {
16
+ return defaultValue;
17
+ }
18
+ // Handle nextval sequences (for serial types) - keep as-is
19
+ if (/^nextval\('.*'::regclass\)$/.test(defaultValue)) {
20
+ return defaultValue;
21
+ }
22
+ // Handle simple literals without type casting
23
+ if (/^'(.*)'::(text|varchar|char|bpchar)$/.test(defaultValue)) {
24
+ return defaultValue.replace(/^'(.*)'::(text|varchar|char|bpchar)$/, "'$1'");
25
+ }
26
+ // Handle numeric literals with type casting
27
+ if (/^'(.*)'::(numeric|decimal|real|double precision|float\d*|int\d*|smallint|bigint|integer)$/.test(defaultValue)) {
28
+ return defaultValue.replace(/^'(.*)'::(numeric|decimal|real|double precision|float\d*|int\d*|smallint|bigint|integer)$/, "$1");
29
+ }
30
+ // Handle boolean literals
31
+ if (defaultValue === "true" || defaultValue === "false") {
32
+ return defaultValue;
33
+ }
34
+ // Handle NULL with type casting
35
+ if (/^NULL::.+$/.test(defaultValue)) {
36
+ return null;
37
+ }
38
+ // Handle array literals - keep as-is since they're complex
39
+ if (/^(ARRAY\[.*\]|\{.*\})/.test(defaultValue)) {
40
+ return defaultValue;
41
+ }
42
+ // For everything else, return as-is
43
+ return defaultValue;
44
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { pullDefault } from "./pullDefault.js";
3
+ describe("pullDefault", () => {
4
+ test("should return null for null values", () => {
5
+ expect(pullDefault(null)).toBe(null);
6
+ });
7
+ test("should preserve timestamp literals as-is", () => {
8
+ expect(pullDefault("'2025-06-28 15:02:55.251517'::timestamp without time zone")).toBe("'2025-06-28 15:02:55.251517'::timestamp without time zone");
9
+ expect(pullDefault("'2025-06-28 15:02:55.251517+00'::timestamptz")).toBe("'2025-06-28 15:02:55.251517+00'::timestamptz");
10
+ expect(pullDefault("'2023-01-01 12:00:00'::timestamp")).toBe("'2023-01-01 12:00:00'::timestamp");
11
+ expect(pullDefault("'2025-06-28 15:02:55'::timestamp with time zone")).toBe("'2025-06-28 15:02:55'::timestamp with time zone");
12
+ });
13
+ test("should preserve SQL function calls", () => {
14
+ expect(pullDefault("now()")).toBe("now()");
15
+ expect(pullDefault("gen_random_uuid()")).toBe("gen_random_uuid()");
16
+ expect(pullDefault("uuid_generate_v4()")).toBe("uuid_generate_v4()");
17
+ expect(pullDefault("current_timestamp()")).toBe("current_timestamp()");
18
+ expect(pullDefault("current_date()")).toBe("current_date()");
19
+ expect(pullDefault("current_time()")).toBe("current_time()");
20
+ });
21
+ test("should preserve nextval sequences for serial types", () => {
22
+ expect(pullDefault("nextval('user_id_seq'::regclass)")).toBe("nextval('user_id_seq'::regclass)");
23
+ expect(pullDefault("nextval('public.post_id_seq'::regclass)")).toBe("nextval('public.post_id_seq'::regclass)");
24
+ });
25
+ test("should handle text literals with type casting", () => {
26
+ expect(pullDefault("'hello'::text")).toBe("'hello'");
27
+ expect(pullDefault("'world'::varchar")).toBe("'world'");
28
+ expect(pullDefault("'test'::char")).toBe("'test'");
29
+ expect(pullDefault("'example'::bpchar")).toBe("'example'");
30
+ });
31
+ test("should handle numeric literals with type casting", () => {
32
+ expect(pullDefault("'123'::numeric")).toBe("123");
33
+ expect(pullDefault("'456'::integer")).toBe("456");
34
+ expect(pullDefault("'789'::bigint")).toBe("789");
35
+ expect(pullDefault("'12.34'::decimal")).toBe("12.34");
36
+ expect(pullDefault("'3.14'::real")).toBe("3.14");
37
+ expect(pullDefault("'2.718'::double precision")).toBe("2.718");
38
+ expect(pullDefault("'42'::smallint")).toBe("42");
39
+ expect(pullDefault("'99.99'::float4")).toBe("99.99");
40
+ expect(pullDefault("'88.88'::int4")).toBe("88.88");
41
+ });
42
+ test("should handle boolean literals", () => {
43
+ expect(pullDefault("true")).toBe("true");
44
+ expect(pullDefault("false")).toBe("false");
45
+ });
46
+ test("should handle NULL with type casting", () => {
47
+ expect(pullDefault("NULL::text")).toBe(null);
48
+ expect(pullDefault("NULL::integer")).toBe(null);
49
+ expect(pullDefault("NULL::timestamp")).toBe(null);
50
+ });
51
+ test("should preserve array literals", () => {
52
+ expect(pullDefault("ARRAY[1, 2, 3]")).toBe("ARRAY[1, 2, 3]");
53
+ expect(pullDefault("ARRAY['foo', 'bar']")).toBe("ARRAY['foo', 'bar']");
54
+ expect(pullDefault("ARRAY[ARRAY[ARRAY['a']]]")).toBe("ARRAY[ARRAY[ARRAY['a']]]");
55
+ expect(pullDefault("{1, 2, 3}")).toBe("{1, 2, 3}");
56
+ expect(pullDefault("{{foo, bar}, {baz, qux}}")).toBe("{{foo, bar}, {baz, qux}}");
57
+ });
58
+ test("should handle complex expressions as-is", () => {
59
+ expect(pullDefault('\'{"key": "value"}\'::jsonb')).toBe('\'{"key": "value"}\'::jsonb');
60
+ expect(pullDefault("'[]'::json")).toBe("'[]'::json");
61
+ expect(pullDefault("'happy'::mood")).toBe("'happy'::mood");
62
+ expect(pullDefault("'admin'::user_role")).toBe("'admin'::user_role");
63
+ expect(pullDefault("'(0,0)'::point")).toBe("'(0,0)'::point");
64
+ expect(pullDefault("'1 day'::interval")).toBe("'1 day'::interval");
65
+ });
66
+ test("should handle money defaults", () => {
67
+ expect(pullDefault("42.42")).toBe("42.42");
68
+ expect(pullDefault("$100.00")).toBe("$100.00");
69
+ });
70
+ test("should handle edge cases", () => {
71
+ expect(pullDefault("")).toBe("");
72
+ expect(pullDefault("0")).toBe("0");
73
+ expect(pullDefault("-1")).toBe("-1");
74
+ expect(pullDefault("'0'")).toBe("'0'");
75
+ });
76
+ test("should handle complex SQL expressions", () => {
77
+ // These should be preserved as-is since they're complex
78
+ expect(pullDefault("CASE WHEN foo = 'bar' THEN 1 ELSE 0 END")).toBe("CASE WHEN foo = 'bar' THEN 1 ELSE 0 END");
79
+ expect(pullDefault("COALESCE(column1, column2, 'default')")).toBe("COALESCE(column1, column2, 'default')");
80
+ expect(pullDefault("LENGTH('hello')")).toBe("LENGTH('hello')");
81
+ });
82
+ });
@@ -0,0 +1,14 @@
1
+ import { Orm } from "@casekit/orm";
2
+ import { type Column } from "./pull/getColumns.js";
3
+ import { type ForeignKey } from "./pull/getForeignKeys.js";
4
+ import { type PrimaryKey } from "./pull/getPrimaryKeys.js";
5
+ import { type UniqueConstraint } from "./pull/getUniqueConstraints.js";
6
+ export type Table = {
7
+ schema: string;
8
+ name: string;
9
+ columns: Column[];
10
+ foreignKeys: ForeignKey[];
11
+ primaryKey: PrimaryKey | null;
12
+ uniqueConstraints: UniqueConstraint[];
13
+ };
14
+ export declare const pull: (db: Orm, schemas: string[]) => Promise<Table[]>;
package/build/pull.js ADDED
@@ -0,0 +1,56 @@
1
+ import { orderBy } from "es-toolkit";
2
+ import { getColumns } from "./pull/getColumns.js";
3
+ import { getForeignKeys } from "./pull/getForeignKeys.js";
4
+ import { getPrimaryKeys } from "./pull/getPrimaryKeys.js";
5
+ import { getUniqueConstraints, } from "./pull/getUniqueConstraints.js";
6
+ import { pullDefault } from "./pull/pullDefault.js";
7
+ export const pull = async (db, schemas) => {
8
+ const [columns, foreignKeys, primaryKeys, uniqueConstraints] = await Promise.all([
9
+ db.query(getColumns(schemas)),
10
+ db.query(getForeignKeys(schemas)),
11
+ db.query(getPrimaryKeys(schemas)),
12
+ db.query(getUniqueConstraints(schemas)),
13
+ ]);
14
+ const tablesMap = new Map();
15
+ for (const column of orderBy(columns, ["ordinalPosition"], ["asc"])) {
16
+ const key = `${column.schema}.${column.table}`;
17
+ if (!tablesMap.has(key)) {
18
+ tablesMap.set(key, {
19
+ schema: column.schema,
20
+ name: column.table,
21
+ columns: [],
22
+ foreignKeys: [],
23
+ primaryKey: null,
24
+ uniqueConstraints: [],
25
+ });
26
+ }
27
+ // Normalize the default value using pullDefault
28
+ const normalizedColumn = {
29
+ ...column,
30
+ default: pullDefault(column.default),
31
+ };
32
+ tablesMap.get(key).columns.push(normalizedColumn);
33
+ }
34
+ for (const fk of foreignKeys) {
35
+ const key = `${fk.schema}.${fk.tableFrom}`;
36
+ const table = tablesMap.get(key);
37
+ if (table) {
38
+ table.foreignKeys.push(fk);
39
+ }
40
+ }
41
+ for (const pk of primaryKeys) {
42
+ const key = `${pk.schema}.${pk.table}`;
43
+ const table = tablesMap.get(key);
44
+ if (table) {
45
+ table.primaryKey = pk;
46
+ }
47
+ }
48
+ for (const uc of uniqueConstraints) {
49
+ const key = `${uc.schema}.${uc.table}`;
50
+ const table = tablesMap.get(key);
51
+ if (table) {
52
+ table.uniqueConstraints.push(uc);
53
+ }
54
+ }
55
+ return orderBy(Array.from(tablesMap.values()), ["schema", "name"], ["asc", "asc"]);
56
+ };
@@ -0,0 +1 @@
1
+ export declare const arrayToSqlArray: (values: unknown[]) => string;
@@ -0,0 +1,6 @@
1
+ export const arrayToSqlArray = (values) => {
2
+ const joined = values
3
+ .map((v) => (Array.isArray(v) ? arrayToSqlArray(v) : JSON.stringify(v)))
4
+ .join(", ");
5
+ return `{ ${joined} }`;
6
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { arrayToSqlArray } from "./arrayToSqlArray.js";
3
+ describe("arrayToSqlArray", () => {
4
+ test("converts a flat array to SQL array format", () => {
5
+ const input = [1, 2, 3];
6
+ const output = arrayToSqlArray(input);
7
+ expect(output).toBe("{ 1, 2, 3 }");
8
+ });
9
+ test("converts a nested array to SQL array format", () => {
10
+ const input = [1, [2, 3], 4];
11
+ const output = arrayToSqlArray(input);
12
+ expect(output).toBe("{ 1, { 2, 3 }, 4 }");
13
+ });
14
+ test("converts an array with strings to SQL array format", () => {
15
+ const input = ["a", "b", "c"];
16
+ const output = arrayToSqlArray(input);
17
+ expect(output).toBe('{ "a", "b", "c" }');
18
+ });
19
+ test("converts a mixed array to SQL array format", () => {
20
+ const input = [1, "b", [2, "c"]];
21
+ const output = arrayToSqlArray(input);
22
+ expect(output).toBe('{ 1, "b", { 2, "c" } }');
23
+ });
24
+ test("converts an empty array to SQL array format", () => {
25
+ const input = [];
26
+ const output = arrayToSqlArray(input);
27
+ expect(output).toBe("{ }");
28
+ });
29
+ });
@@ -0,0 +1,2 @@
1
+ import { SQLStatement } from "@casekit/orm";
2
+ export declare const createExtensionsSql: (schema: string, extension: string) => SQLStatement;
@@ -0,0 +1,4 @@
1
+ import { sql } from "@casekit/orm";
2
+ export const createExtensionsSql = (schema, extension) => {
3
+ return sql `CREATE EXTENSION IF NOT EXISTS ${sql.ident(extension)} SCHEMA ${sql.ident(schema)};`;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
2
+ import { orm } from "@casekit/orm";
3
+ import { createExtensionsSql } from "./createExtensionSql.js";
4
+ describe("createExtensionsSql", () => {
5
+ const db = orm({ models: {} });
6
+ beforeAll(async () => {
7
+ await db.connect();
8
+ });
9
+ afterAll(async () => {
10
+ await db.close();
11
+ });
12
+ test("creates SQL statement for creating an extension", async () => {
13
+ const statement = createExtensionsSql("foo", "uuid-ossp");
14
+ expect(statement.text).toEqual('CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA "foo";');
15
+ expect(statement.values).toEqual([]);
16
+ await db.transact(async (db) => {
17
+ await expect(db.query(statement)).resolves.not.toThrow();
18
+ }, { rollback: true });
19
+ });
20
+ });
@@ -0,0 +1,3 @@
1
+ import { SQLStatement } from "@casekit/orm";
2
+ import { NormalizedForeignKeyDefinition, NormalizedModelDefinition } from "@casekit/orm-config";
3
+ export declare const createForeignKeyConstraintSql: (model: NormalizedModelDefinition, fk: NormalizedForeignKeyDefinition) => SQLStatement<import("pg").QueryResultRow>;
@@ -0,0 +1,17 @@
1
+ import { SQLStatement, sql } from "@casekit/orm";
2
+ export const createForeignKeyConstraintSql = (model, fk) => {
3
+ const statement = sql `
4
+ ALTER TABLE ${sql.ident(model.schema)}.${sql.ident(model.table)}
5
+ ADD CONSTRAINT ${sql.ident(fk.name)}
6
+ FOREIGN KEY (${sql.join(fk.columns.map(sql.ident), ", ")})
7
+ REFERENCES ${sql.ident(fk.references.schema)}.${sql.ident(fk.references.table)} (${sql.join(fk.references.columns.map(sql.ident), ", ")})`;
8
+ if (fk.onDelete) {
9
+ statement.append `
10
+ ON DELETE ${new SQLStatement(fk.onDelete)}`;
11
+ }
12
+ if (fk.onUpdate) {
13
+ statement.append `
14
+ ON UPDATE ${new SQLStatement(fk.onUpdate)}`;
15
+ }
16
+ return statement;
17
+ };
@@ -0,0 +1 @@
1
+ export {};