@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.
- package/build/drop.d.ts +2 -0
- package/build/drop.js +12 -0
- package/build/index.d.ts +7 -0
- package/build/index.js +8 -0
- package/build/pull/getColumns.d.ts +34 -0
- package/build/pull/getColumns.js +83 -0
- package/build/pull/getColumns.test.d.ts +1 -0
- package/build/pull/getColumns.test.js +320 -0
- package/build/pull/getForeignKeys.d.ts +18 -0
- package/build/pull/getForeignKeys.js +72 -0
- package/build/pull/getForeignKeys.test.d.ts +1 -0
- package/build/pull/getForeignKeys.test.js +86 -0
- package/build/pull/getPrimaryKeys.d.ts +14 -0
- package/build/pull/getPrimaryKeys.js +30 -0
- package/build/pull/getPrimaryKeys.test.d.ts +1 -0
- package/build/pull/getPrimaryKeys.test.js +87 -0
- package/build/pull/getUniqueConstraints.d.ts +14 -0
- package/build/pull/getUniqueConstraints.js +27 -0
- package/build/pull/getUniqueConstraints.test.d.ts +1 -0
- package/build/pull/getUniqueConstraints.test.js +99 -0
- package/build/pull/index.d.ts +4 -0
- package/build/pull/index.js +1 -0
- package/build/pull/pullDefault.d.ts +6 -0
- package/build/pull/pullDefault.js +44 -0
- package/build/pull/pullDefault.test.d.ts +1 -0
- package/build/pull/pullDefault.test.js +82 -0
- package/build/pull.d.ts +14 -0
- package/build/pull.js +56 -0
- package/build/push/arrayToSqlArray.d.ts +1 -0
- package/build/push/arrayToSqlArray.js +6 -0
- package/build/push/arrayToSqlArray.test.d.ts +1 -0
- package/build/push/arrayToSqlArray.test.js +29 -0
- package/build/push/createExtensionSql.d.ts +2 -0
- package/build/push/createExtensionSql.js +4 -0
- package/build/push/createExtensionSql.test.d.ts +1 -0
- package/build/push/createExtensionSql.test.js +20 -0
- package/build/push/createForeignKeyConstraintSql.d.ts +3 -0
- package/build/push/createForeignKeyConstraintSql.js +17 -0
- package/build/push/createForeignKeyConstraintSql.test.d.ts +1 -0
- package/build/push/createForeignKeyConstraintSql.test.js +142 -0
- package/build/push/createSchemaSql.d.ts +1 -0
- package/build/push/createSchemaSql.js +7 -0
- package/build/push/createSchemaSql.test.d.ts +1 -0
- package/build/push/createSchemaSql.test.js +26 -0
- package/build/push/createTableSql.d.ts +3 -0
- package/build/push/createTableSql.js +38 -0
- package/build/push/createTableSql.test.d.ts +1 -0
- package/build/push/createTableSql.test.js +110 -0
- package/build/push/createUniqueConstraintSql.d.ts +2 -0
- package/build/push/createUniqueConstraintSql.js +17 -0
- package/build/push/createUniqueConstraintSql.test.d.ts +1 -0
- package/build/push/createUniqueConstraintSql.test.js +105 -0
- package/build/push.d.ts +2 -0
- package/build/push.js +38 -0
- package/package.json +59 -0
package/build/drop.d.ts
ADDED
package/build/drop.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { sql } from "@casekit/orm";
|
|
2
|
+
export const drop = async (db) => {
|
|
3
|
+
const schemas = new Set(Object.values(db.config.models).map((model) => model.schema));
|
|
4
|
+
await db.transact(async (db) => {
|
|
5
|
+
for (const schema of schemas.values()) {
|
|
6
|
+
console.log(` - Dropping schema ${schema}`);
|
|
7
|
+
await db.query `
|
|
8
|
+
DROP SCHEMA IF EXISTS ${sql.ident(schema)} CASCADE;
|
|
9
|
+
`;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
};
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const migrate: {
|
|
2
|
+
drop: (db: import("@casekit/orm").Orm) => Promise<void>;
|
|
3
|
+
push: (db: import("@casekit/orm").Orm) => Promise<void>;
|
|
4
|
+
pull: (db: import("@casekit/orm").Orm, schemas: string[]) => Promise<import("#pull.js").Table[]>;
|
|
5
|
+
};
|
|
6
|
+
export type { Table } from "#pull.js";
|
|
7
|
+
export type { Column, ForeignKey, PrimaryKey, UniqueConstraint, } from "#pull/index.js";
|
package/build/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ColumnSchema: z.ZodObject<{
|
|
3
|
+
schema: z.ZodString;
|
|
4
|
+
table: z.ZodString;
|
|
5
|
+
column: z.ZodString;
|
|
6
|
+
ordinalPosition: z.ZodNullable<z.ZodNumber>;
|
|
7
|
+
type: z.ZodString;
|
|
8
|
+
default: z.ZodNullable<z.ZodString>;
|
|
9
|
+
nullable: z.ZodBoolean;
|
|
10
|
+
udtSchema: z.ZodString;
|
|
11
|
+
udt: z.ZodString;
|
|
12
|
+
elementType: z.ZodNullable<z.ZodString>;
|
|
13
|
+
elementTypeSchema: z.ZodNullable<z.ZodString>;
|
|
14
|
+
cardinality: z.ZodNumber;
|
|
15
|
+
size: z.ZodNullable<z.ZodNumber>;
|
|
16
|
+
isSerial: z.ZodBoolean;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
export type Column = z.infer<typeof ColumnSchema>;
|
|
19
|
+
export declare const getColumns: (schemas: string[]) => import("@casekit/orm").SQLStatement<{
|
|
20
|
+
schema: string;
|
|
21
|
+
table: string;
|
|
22
|
+
column: string;
|
|
23
|
+
ordinalPosition: number | null;
|
|
24
|
+
type: string;
|
|
25
|
+
default: string | null;
|
|
26
|
+
nullable: boolean;
|
|
27
|
+
udtSchema: string;
|
|
28
|
+
udt: string;
|
|
29
|
+
elementType: string | null;
|
|
30
|
+
elementTypeSchema: string | null;
|
|
31
|
+
cardinality: number;
|
|
32
|
+
size: number | null;
|
|
33
|
+
isSerial: boolean;
|
|
34
|
+
}>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { sql } from "@casekit/orm";
|
|
3
|
+
export const ColumnSchema = z.object({
|
|
4
|
+
schema: z.string(),
|
|
5
|
+
table: z.string(),
|
|
6
|
+
column: z.string(),
|
|
7
|
+
ordinalPosition: z.number().nullable(),
|
|
8
|
+
type: z.string(),
|
|
9
|
+
default: z.string().nullable(),
|
|
10
|
+
nullable: z.boolean(),
|
|
11
|
+
udtSchema: z.string(),
|
|
12
|
+
udt: z.string(),
|
|
13
|
+
elementType: z.string().nullable(),
|
|
14
|
+
elementTypeSchema: z.string().nullable(),
|
|
15
|
+
cardinality: z.number(),
|
|
16
|
+
size: z.number().nullable(),
|
|
17
|
+
isSerial: z.boolean(),
|
|
18
|
+
});
|
|
19
|
+
export const getColumns = (schemas) => sql(ColumnSchema) `
|
|
20
|
+
SELECT
|
|
21
|
+
c.table_schema AS "schema",
|
|
22
|
+
c.table_name AS "table",
|
|
23
|
+
c.column_name AS "column",
|
|
24
|
+
c.ordinal_position AS "ordinalPosition",
|
|
25
|
+
CASE
|
|
26
|
+
WHEN c.udt_name = 'int2vector' THEN 'int2vector'
|
|
27
|
+
WHEN c.udt_name = 'oidvector' THEN 'oidvector'
|
|
28
|
+
WHEN c.data_type = 'ARRAY' AND c.udt_name LIKE '\_int2vector' THEN 'int2vector[]'
|
|
29
|
+
ELSE c.data_type
|
|
30
|
+
END AS "type",
|
|
31
|
+
c.column_default AS "default",
|
|
32
|
+
c.is_nullable = 'YES' AS "nullable",
|
|
33
|
+
c.udt_schema AS "udtSchema",
|
|
34
|
+
c.udt_name AS "udt",
|
|
35
|
+
e.data_type AS "elementType",
|
|
36
|
+
e.udt_schema AS "elementTypeSchema",
|
|
37
|
+
pa.attndims AS cardinality,
|
|
38
|
+
c.character_maximum_length AS "size",
|
|
39
|
+
COALESCE(seq_owned.is_serial, false) AS "isSerial"
|
|
40
|
+
FROM
|
|
41
|
+
information_schema.tables t
|
|
42
|
+
JOIN information_schema.columns c ON t.table_name = c.table_name
|
|
43
|
+
AND t.table_schema = c.table_schema
|
|
44
|
+
AND t.table_catalog = c.table_catalog
|
|
45
|
+
JOIN pg_catalog.pg_class pc ON pc.relname = c.table_name
|
|
46
|
+
JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace
|
|
47
|
+
AND pn.nspname = c.table_schema
|
|
48
|
+
JOIN pg_catalog.pg_attribute pa ON pa.attrelid = pc.oid
|
|
49
|
+
AND pa.attname = c.column_name
|
|
50
|
+
LEFT JOIN information_schema.element_types e ON c.table_catalog = e.object_catalog
|
|
51
|
+
AND c.table_schema = e.object_schema
|
|
52
|
+
AND c.table_name = e.object_name
|
|
53
|
+
AND e.object_type = 'TABLE'
|
|
54
|
+
AND e.collection_type_identifier = c.dtd_identifier
|
|
55
|
+
AND c.data_type = 'ARRAY'
|
|
56
|
+
LEFT JOIN (
|
|
57
|
+
SELECT
|
|
58
|
+
pn.nspname AS schema_name,
|
|
59
|
+
pc.relname AS table_name,
|
|
60
|
+
pa.attname AS column_name,
|
|
61
|
+
true AS is_serial
|
|
62
|
+
FROM
|
|
63
|
+
pg_depend pd
|
|
64
|
+
JOIN pg_class pc ON pd.refobjid = pc.oid AND pc.relkind = 'r'
|
|
65
|
+
JOIN pg_attribute pa ON pd.refobjid = pa.attrelid AND pd.refobjsubid = pa.attnum
|
|
66
|
+
JOIN pg_namespace pn ON pc.relnamespace = pn.oid
|
|
67
|
+
JOIN pg_class seq ON pd.objid = seq.oid AND seq.relkind = 'S'
|
|
68
|
+
WHERE
|
|
69
|
+
pd.deptype = 'a'
|
|
70
|
+
AND pd.classid = 'pg_class'::regclass
|
|
71
|
+
AND pd.refclassid = 'pg_class'::regclass
|
|
72
|
+
AND pn.nspname IN (${schemas})
|
|
73
|
+
) seq_owned ON seq_owned.schema_name = c.table_schema
|
|
74
|
+
AND seq_owned.table_name = c.table_name
|
|
75
|
+
AND seq_owned.column_name = c.column_name
|
|
76
|
+
WHERE
|
|
77
|
+
t.table_schema IN (${schemas})
|
|
78
|
+
AND t.table_type = 'BASE TABLE'
|
|
79
|
+
AND pa.attnum > 0
|
|
80
|
+
ORDER BY
|
|
81
|
+
t.table_name,
|
|
82
|
+
c.ordinal_position
|
|
83
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
|
|
2
|
+
import { orm, sql } from "@casekit/orm";
|
|
3
|
+
import { getColumns } from "./getColumns.js";
|
|
4
|
+
describe("getColumns", () => {
|
|
5
|
+
const db = orm({
|
|
6
|
+
schema: "migrate-get-tables-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-tables-test" CASCADE;`;
|
|
17
|
+
await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-tables-test";`;
|
|
18
|
+
});
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await db.query `DROP SCHEMA IF EXISTS "migrate-get-tables-test" CASCADE;`;
|
|
21
|
+
});
|
|
22
|
+
test("with an empty database it returns no tables or columns", async () => {
|
|
23
|
+
const statement = getColumns(["migrate-get-tables-test"]);
|
|
24
|
+
const result = await db.query(statement);
|
|
25
|
+
expect(result).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
test.for([
|
|
28
|
+
[
|
|
29
|
+
"handles uuid columns",
|
|
30
|
+
sql `
|
|
31
|
+
CREATE TABLE "migrate-get-tables-test"."uuid_test" (
|
|
32
|
+
"uuid_col" UUID DEFAULT gen_random_uuid(),
|
|
33
|
+
"uuid_col_notnull" UUID NOT NULL
|
|
34
|
+
);
|
|
35
|
+
`,
|
|
36
|
+
[
|
|
37
|
+
expect.objectContaining({
|
|
38
|
+
type: "uuid",
|
|
39
|
+
default: "gen_random_uuid()",
|
|
40
|
+
nullable: true,
|
|
41
|
+
}),
|
|
42
|
+
expect.objectContaining({
|
|
43
|
+
type: "uuid",
|
|
44
|
+
nullable: false,
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
"handles serial columns",
|
|
50
|
+
sql `
|
|
51
|
+
CREATE TABLE "migrate-get-tables-test"."serial_test" (
|
|
52
|
+
"smallserial_col" SMALLSERIAL,
|
|
53
|
+
"serial_col" SERIAL,
|
|
54
|
+
"bigserial_col" BIGSERIAL
|
|
55
|
+
);
|
|
56
|
+
`,
|
|
57
|
+
[
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
type: "smallint",
|
|
60
|
+
default: expect.stringContaining("nextval"),
|
|
61
|
+
nullable: false,
|
|
62
|
+
isSerial: true,
|
|
63
|
+
}),
|
|
64
|
+
expect.objectContaining({
|
|
65
|
+
type: "integer",
|
|
66
|
+
default: expect.stringContaining("nextval"),
|
|
67
|
+
nullable: false,
|
|
68
|
+
isSerial: true,
|
|
69
|
+
}),
|
|
70
|
+
expect.objectContaining({
|
|
71
|
+
type: "bigint",
|
|
72
|
+
default: expect.stringContaining("nextval"),
|
|
73
|
+
nullable: false,
|
|
74
|
+
isSerial: true,
|
|
75
|
+
}),
|
|
76
|
+
],
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
"handles boolean columns",
|
|
80
|
+
sql `
|
|
81
|
+
CREATE TABLE "migrate-get-tables-test"."boolean_test" (
|
|
82
|
+
"bool_col" BOOLEAN DEFAULT true
|
|
83
|
+
);
|
|
84
|
+
`,
|
|
85
|
+
[
|
|
86
|
+
expect.objectContaining({
|
|
87
|
+
type: "boolean",
|
|
88
|
+
default: "true",
|
|
89
|
+
}),
|
|
90
|
+
],
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
"handles numeric columns",
|
|
94
|
+
sql `
|
|
95
|
+
CREATE TABLE "migrate-get-tables-test"."numeric_test" (
|
|
96
|
+
"numeric_col" NUMERIC(10,2)
|
|
97
|
+
);
|
|
98
|
+
`,
|
|
99
|
+
[
|
|
100
|
+
expect.objectContaining({
|
|
101
|
+
type: "numeric",
|
|
102
|
+
udt: "numeric",
|
|
103
|
+
}),
|
|
104
|
+
],
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
"handles various numeric and decimal types",
|
|
108
|
+
sql `
|
|
109
|
+
CREATE TABLE "migrate-get-tables-test"."number_test" (
|
|
110
|
+
"smallint_col" SMALLINT,
|
|
111
|
+
"integer_col" INTEGER,
|
|
112
|
+
"bigint_col" BIGINT,
|
|
113
|
+
"decimal_col" DECIMAL(10,2),
|
|
114
|
+
"real_col" REAL,
|
|
115
|
+
"double_col" DOUBLE PRECISION
|
|
116
|
+
);
|
|
117
|
+
`,
|
|
118
|
+
[
|
|
119
|
+
expect.objectContaining({ type: "smallint", udt: "int2" }),
|
|
120
|
+
expect.objectContaining({ type: "integer", udt: "int4" }),
|
|
121
|
+
expect.objectContaining({ type: "bigint", udt: "int8" }),
|
|
122
|
+
expect.objectContaining({ type: "numeric", udt: "numeric" }),
|
|
123
|
+
expect.objectContaining({ type: "real", udt: "float4" }),
|
|
124
|
+
expect.objectContaining({
|
|
125
|
+
type: "double precision",
|
|
126
|
+
udt: "float8",
|
|
127
|
+
}),
|
|
128
|
+
],
|
|
129
|
+
],
|
|
130
|
+
[
|
|
131
|
+
"handles character types",
|
|
132
|
+
sql `
|
|
133
|
+
CREATE TABLE "migrate-get-tables-test"."char_test" (
|
|
134
|
+
"char_col" CHAR(10),
|
|
135
|
+
"varchar_col" VARCHAR(50),
|
|
136
|
+
"text_col" TEXT,
|
|
137
|
+
"name_col" NAME
|
|
138
|
+
);
|
|
139
|
+
`,
|
|
140
|
+
[
|
|
141
|
+
expect.objectContaining({ type: "character", size: 10 }),
|
|
142
|
+
expect.objectContaining({
|
|
143
|
+
type: "character varying",
|
|
144
|
+
size: 50,
|
|
145
|
+
}),
|
|
146
|
+
expect.objectContaining({ type: "text" }),
|
|
147
|
+
expect.objectContaining({ type: "name" }),
|
|
148
|
+
],
|
|
149
|
+
],
|
|
150
|
+
[
|
|
151
|
+
"handles date and time types",
|
|
152
|
+
sql `
|
|
153
|
+
CREATE TABLE "migrate-get-tables-test"."datetime_test" (
|
|
154
|
+
"date_col" DATE,
|
|
155
|
+
"time_col" TIME,
|
|
156
|
+
"timetz_col" TIME WITH TIME ZONE,
|
|
157
|
+
"timestamp_col" TIMESTAMP,
|
|
158
|
+
"timestamptz_col" TIMESTAMP WITH TIME ZONE,
|
|
159
|
+
"interval_col" INTERVAL
|
|
160
|
+
);
|
|
161
|
+
`,
|
|
162
|
+
[
|
|
163
|
+
expect.objectContaining({ type: "date" }),
|
|
164
|
+
expect.objectContaining({ type: "time without time zone" }),
|
|
165
|
+
expect.objectContaining({ type: "time with time zone" }),
|
|
166
|
+
expect.objectContaining({
|
|
167
|
+
type: "timestamp without time zone",
|
|
168
|
+
}),
|
|
169
|
+
expect.objectContaining({ type: "timestamp with time zone" }),
|
|
170
|
+
expect.objectContaining({ type: "interval" }),
|
|
171
|
+
],
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
"handles array columns",
|
|
175
|
+
sql `
|
|
176
|
+
CREATE TABLE "migrate-get-tables-test"."array_test" (
|
|
177
|
+
"array_col" INTEGER[]
|
|
178
|
+
);
|
|
179
|
+
`,
|
|
180
|
+
[
|
|
181
|
+
expect.objectContaining({
|
|
182
|
+
type: "ARRAY",
|
|
183
|
+
elementType: "integer",
|
|
184
|
+
cardinality: 1,
|
|
185
|
+
}),
|
|
186
|
+
],
|
|
187
|
+
],
|
|
188
|
+
[
|
|
189
|
+
"handles multi-dimensional array columns",
|
|
190
|
+
sql `
|
|
191
|
+
CREATE TABLE "migrate-get-tables-test"."array_test" (
|
|
192
|
+
"multi_dim_array_col" TEXT[][][] NOT NULL DEFAULT ARRAY[ARRAY[ARRAY['a']]]
|
|
193
|
+
);
|
|
194
|
+
`,
|
|
195
|
+
[
|
|
196
|
+
expect.objectContaining({
|
|
197
|
+
type: "ARRAY",
|
|
198
|
+
elementType: "text",
|
|
199
|
+
cardinality: 3,
|
|
200
|
+
nullable: false,
|
|
201
|
+
default: "ARRAY[ARRAY[ARRAY['a'::text]]]",
|
|
202
|
+
}),
|
|
203
|
+
],
|
|
204
|
+
],
|
|
205
|
+
[
|
|
206
|
+
"handles json columns",
|
|
207
|
+
sql `
|
|
208
|
+
CREATE TABLE "migrate-get-tables-test"."json_test" (
|
|
209
|
+
"json_col" JSON
|
|
210
|
+
);
|
|
211
|
+
`,
|
|
212
|
+
[
|
|
213
|
+
expect.objectContaining({
|
|
214
|
+
type: "json",
|
|
215
|
+
}),
|
|
216
|
+
],
|
|
217
|
+
],
|
|
218
|
+
[
|
|
219
|
+
"handles enum columns",
|
|
220
|
+
sql `
|
|
221
|
+
CREATE TYPE "migrate-get-tables-test"."mood" AS ENUM ('happy', 'sad');
|
|
222
|
+
CREATE TABLE "migrate-get-tables-test"."enum_test" (
|
|
223
|
+
"enum_col" "migrate-get-tables-test"."mood"
|
|
224
|
+
);
|
|
225
|
+
`,
|
|
226
|
+
[
|
|
227
|
+
expect.objectContaining({
|
|
228
|
+
type: "USER-DEFINED",
|
|
229
|
+
udtSchema: "migrate-get-tables-test",
|
|
230
|
+
udt: "mood",
|
|
231
|
+
}),
|
|
232
|
+
],
|
|
233
|
+
],
|
|
234
|
+
[
|
|
235
|
+
"handles composite type columns",
|
|
236
|
+
sql `
|
|
237
|
+
CREATE TYPE "migrate-get-tables-test"."complex" AS (x double precision, y double precision);
|
|
238
|
+
CREATE TABLE "migrate-get-tables-test"."composite_test" (
|
|
239
|
+
"complex_col" "migrate-get-tables-test"."complex"
|
|
240
|
+
);
|
|
241
|
+
`,
|
|
242
|
+
[
|
|
243
|
+
expect.objectContaining({
|
|
244
|
+
type: "USER-DEFINED",
|
|
245
|
+
udtSchema: "migrate-get-tables-test",
|
|
246
|
+
udt: "complex",
|
|
247
|
+
}),
|
|
248
|
+
],
|
|
249
|
+
],
|
|
250
|
+
[
|
|
251
|
+
"handles currency columns",
|
|
252
|
+
sql `
|
|
253
|
+
CREATE TABLE "migrate-get-tables-test"."currency_test" (
|
|
254
|
+
"money_col" MONEY DEFAULT 42.42
|
|
255
|
+
);
|
|
256
|
+
`,
|
|
257
|
+
[
|
|
258
|
+
expect.objectContaining({
|
|
259
|
+
type: "money",
|
|
260
|
+
default: "42.42",
|
|
261
|
+
}),
|
|
262
|
+
],
|
|
263
|
+
],
|
|
264
|
+
[
|
|
265
|
+
"handles geometric columns",
|
|
266
|
+
sql `
|
|
267
|
+
CREATE TABLE "migrate-get-tables-test"."geometry_test" (
|
|
268
|
+
"point_col" POINT,
|
|
269
|
+
"line_col" LINE,
|
|
270
|
+
"polygon_col" POLYGON
|
|
271
|
+
);
|
|
272
|
+
`,
|
|
273
|
+
[
|
|
274
|
+
expect.objectContaining({ type: "point" }),
|
|
275
|
+
expect.objectContaining({ type: "line" }),
|
|
276
|
+
expect.objectContaining({ type: "polygon" }),
|
|
277
|
+
],
|
|
278
|
+
],
|
|
279
|
+
[
|
|
280
|
+
"handles network address columns",
|
|
281
|
+
sql `
|
|
282
|
+
CREATE TABLE "migrate-get-tables-test"."network_test" (
|
|
283
|
+
"inet_col" INET,
|
|
284
|
+
"cidr_col" CIDR,
|
|
285
|
+
"macaddr_col" MACADDR
|
|
286
|
+
);
|
|
287
|
+
`,
|
|
288
|
+
[
|
|
289
|
+
expect.objectContaining({ type: "inet" }),
|
|
290
|
+
expect.objectContaining({ type: "cidr" }),
|
|
291
|
+
expect.objectContaining({ type: "macaddr" }),
|
|
292
|
+
],
|
|
293
|
+
],
|
|
294
|
+
[
|
|
295
|
+
"handles sql now() default values correctly",
|
|
296
|
+
sql `
|
|
297
|
+
CREATE TABLE "migrate-get-tables-test"."timestamp_defaults_test" (
|
|
298
|
+
"created_at" TIMESTAMP DEFAULT now(),
|
|
299
|
+
"updated_at" TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
300
|
+
);
|
|
301
|
+
`,
|
|
302
|
+
[
|
|
303
|
+
expect.objectContaining({
|
|
304
|
+
type: "timestamp without time zone",
|
|
305
|
+
default: "now()",
|
|
306
|
+
}),
|
|
307
|
+
expect.objectContaining({
|
|
308
|
+
type: "timestamp with time zone",
|
|
309
|
+
default: "now()",
|
|
310
|
+
}),
|
|
311
|
+
],
|
|
312
|
+
],
|
|
313
|
+
])("%s", async ([_, query, expected]) => {
|
|
314
|
+
await db.transact(async (db) => {
|
|
315
|
+
await db.query(query);
|
|
316
|
+
const result = await db.query(getColumns(["migrate-get-tables-test"]));
|
|
317
|
+
expect(result).toEqual(expected);
|
|
318
|
+
}, { rollback: true });
|
|
319
|
+
});
|
|
320
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ForeignKeySchema: z.ZodObject<{
|
|
3
|
+
schema: z.ZodString;
|
|
4
|
+
constraintName: z.ZodString;
|
|
5
|
+
tableFrom: z.ZodString;
|
|
6
|
+
columnsFrom: z.ZodArray<z.ZodString>;
|
|
7
|
+
tableTo: z.ZodString;
|
|
8
|
+
columnsTo: z.ZodArray<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type ForeignKey = z.infer<typeof ForeignKeySchema>;
|
|
11
|
+
export declare const getForeignKeys: (schemas: string[]) => import("@casekit/orm").SQLStatement<{
|
|
12
|
+
schema: string;
|
|
13
|
+
constraintName: string;
|
|
14
|
+
tableFrom: string;
|
|
15
|
+
columnsFrom: string[];
|
|
16
|
+
tableTo: string;
|
|
17
|
+
columnsTo: string[];
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { sql } from "@casekit/orm";
|
|
3
|
+
export const ForeignKeySchema = z.object({
|
|
4
|
+
schema: z.string(),
|
|
5
|
+
constraintName: z.string(),
|
|
6
|
+
tableFrom: z.string(),
|
|
7
|
+
columnsFrom: z.array(z.string()),
|
|
8
|
+
tableTo: z.string(),
|
|
9
|
+
columnsTo: z.array(z.string()),
|
|
10
|
+
});
|
|
11
|
+
export const getForeignKeys = (schemas) => sql(ForeignKeySchema) `
|
|
12
|
+
SELECT
|
|
13
|
+
nspname AS "schema",
|
|
14
|
+
conname AS "constraintName",
|
|
15
|
+
table_from AS "tableFrom",
|
|
16
|
+
array_agg(columns_from::text ORDER BY ordinality) AS "columnsFrom",
|
|
17
|
+
table_to AS "tableTo",
|
|
18
|
+
array_agg(columns_to::text ORDER BY ordinality) AS "columnsTo"
|
|
19
|
+
FROM (
|
|
20
|
+
SELECT
|
|
21
|
+
conname,
|
|
22
|
+
c.relname AS table_from,
|
|
23
|
+
a.attname AS columns_from,
|
|
24
|
+
cf.relname AS table_to,
|
|
25
|
+
af.attname AS columns_to,
|
|
26
|
+
n.nspname,
|
|
27
|
+
ss2.ordinality
|
|
28
|
+
FROM
|
|
29
|
+
pg_attribute AS af,
|
|
30
|
+
pg_attribute AS a,
|
|
31
|
+
pg_class c,
|
|
32
|
+
pg_class cf,
|
|
33
|
+
pg_namespace n,
|
|
34
|
+
(
|
|
35
|
+
SELECT
|
|
36
|
+
conname,
|
|
37
|
+
conrelid,
|
|
38
|
+
confrelid,
|
|
39
|
+
conkey[i] AS conkey,
|
|
40
|
+
confkey[i] AS confkey,
|
|
41
|
+
i AS ordinality
|
|
42
|
+
FROM (
|
|
43
|
+
SELECT
|
|
44
|
+
conname,
|
|
45
|
+
conrelid,
|
|
46
|
+
confrelid,
|
|
47
|
+
conkey,
|
|
48
|
+
confkey,
|
|
49
|
+
generate_series(1, array_upper(conkey, 1)) AS i
|
|
50
|
+
FROM
|
|
51
|
+
pg_constraint
|
|
52
|
+
WHERE
|
|
53
|
+
contype = 'f') AS ss) AS ss2
|
|
54
|
+
WHERE
|
|
55
|
+
af.attnum = confkey
|
|
56
|
+
AND af.attrelid = confrelid
|
|
57
|
+
AND a.attnum = conkey
|
|
58
|
+
AND a.attrelid = conrelid
|
|
59
|
+
AND a.attrelid = c.oid
|
|
60
|
+
AND af.attrelid = cf.oid
|
|
61
|
+
AND c.relnamespace = n.oid
|
|
62
|
+
AND n.nspname IN (${schemas})) AS ss3
|
|
63
|
+
GROUP BY
|
|
64
|
+
nspname,
|
|
65
|
+
conname,
|
|
66
|
+
table_to,
|
|
67
|
+
table_from
|
|
68
|
+
ORDER BY
|
|
69
|
+
nspname,
|
|
70
|
+
table_from,
|
|
71
|
+
conname
|
|
72
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
|
|
2
|
+
import { orm, sql } from "@casekit/orm";
|
|
3
|
+
import { getForeignKeys } from "./getForeignKeys.js";
|
|
4
|
+
describe("getForeignKeys", () => {
|
|
5
|
+
const db = orm({
|
|
6
|
+
schema: "migrate-get-foreign-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-foreign-keys-test" CASCADE;`;
|
|
17
|
+
await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-foreign-keys-test";`;
|
|
18
|
+
});
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await db.query `DROP SCHEMA IF EXISTS "migrate-get-foreign-keys-test" CASCADE;`;
|
|
21
|
+
});
|
|
22
|
+
test("with an empty database it returns no foreign keys", async () => {
|
|
23
|
+
const statement = getForeignKeys(["migrate-get-foreign-keys-test"]);
|
|
24
|
+
const result = await db.query(statement);
|
|
25
|
+
expect(result).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
test("returns foreign key relationships", async () => {
|
|
28
|
+
await db.transact(async (db) => {
|
|
29
|
+
await db.query(sql `
|
|
30
|
+
CREATE TABLE "migrate-get-foreign-keys-test"."users" (
|
|
31
|
+
"id" SERIAL PRIMARY KEY,
|
|
32
|
+
"name" TEXT NOT NULL
|
|
33
|
+
);
|
|
34
|
+
`);
|
|
35
|
+
await db.query(sql `
|
|
36
|
+
CREATE TABLE "migrate-get-foreign-keys-test"."posts" (
|
|
37
|
+
"id" SERIAL PRIMARY KEY,
|
|
38
|
+
"title" TEXT NOT NULL,
|
|
39
|
+
"user_id" INTEGER NOT NULL,
|
|
40
|
+
CONSTRAINT "fk_posts_user_id" FOREIGN KEY ("user_id") REFERENCES "migrate-get-foreign-keys-test"."users" ("id")
|
|
41
|
+
);
|
|
42
|
+
`);
|
|
43
|
+
const result = await db.query(getForeignKeys(["migrate-get-foreign-keys-test"]));
|
|
44
|
+
expect(result).toEqual([
|
|
45
|
+
expect.objectContaining({
|
|
46
|
+
schema: "migrate-get-foreign-keys-test",
|
|
47
|
+
constraintName: "fk_posts_user_id",
|
|
48
|
+
tableFrom: "posts",
|
|
49
|
+
columnsFrom: ["user_id"],
|
|
50
|
+
tableTo: "users",
|
|
51
|
+
columnsTo: ["id"],
|
|
52
|
+
}),
|
|
53
|
+
]);
|
|
54
|
+
}, { rollback: true });
|
|
55
|
+
});
|
|
56
|
+
test("handles composite foreign keys", async () => {
|
|
57
|
+
await db.transact(async (db) => {
|
|
58
|
+
await db.query(sql `
|
|
59
|
+
CREATE TABLE "migrate-get-foreign-keys-test"."companies" (
|
|
60
|
+
"id" INTEGER NOT NULL,
|
|
61
|
+
"code" TEXT NOT NULL,
|
|
62
|
+
PRIMARY KEY ("id", "code")
|
|
63
|
+
);
|
|
64
|
+
`);
|
|
65
|
+
await db.query(sql `
|
|
66
|
+
CREATE TABLE "migrate-get-foreign-keys-test"."employees" (
|
|
67
|
+
"id" SERIAL PRIMARY KEY,
|
|
68
|
+
"company_id" INTEGER NOT NULL,
|
|
69
|
+
"company_code" TEXT NOT NULL,
|
|
70
|
+
CONSTRAINT "fk_employees_company" FOREIGN KEY ("company_id", "company_code") REFERENCES "migrate-get-foreign-keys-test"."companies" ("id", "code")
|
|
71
|
+
);
|
|
72
|
+
`);
|
|
73
|
+
const result = await db.query(getForeignKeys(["migrate-get-foreign-keys-test"]));
|
|
74
|
+
expect(result).toEqual([
|
|
75
|
+
expect.objectContaining({
|
|
76
|
+
schema: "migrate-get-foreign-keys-test",
|
|
77
|
+
constraintName: "fk_employees_company",
|
|
78
|
+
tableFrom: "employees",
|
|
79
|
+
columnsFrom: ["company_id", "company_code"],
|
|
80
|
+
tableTo: "companies",
|
|
81
|
+
columnsTo: ["id", "code"],
|
|
82
|
+
}),
|
|
83
|
+
]);
|
|
84
|
+
}, { rollback: true });
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const PrimaryKeySchema: z.ZodObject<{
|
|
3
|
+
schema: z.ZodString;
|
|
4
|
+
table: z.ZodString;
|
|
5
|
+
constraintName: z.ZodString;
|
|
6
|
+
columns: z.ZodArray<z.ZodString>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export type PrimaryKey = z.infer<typeof PrimaryKeySchema>;
|
|
9
|
+
export declare const getPrimaryKeys: (schemas: string[]) => import("@casekit/orm").SQLStatement<{
|
|
10
|
+
schema: string;
|
|
11
|
+
table: string;
|
|
12
|
+
constraintName: string;
|
|
13
|
+
columns: string[];
|
|
14
|
+
}>;
|