@contember/engine-system-api 1.2.0 → 1.3.0-alpha.13
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/src/SystemContainer.d.ts +5 -12
- package/dist/src/SystemContainer.d.ts.map +1 -1
- package/dist/src/SystemContainer.js +4 -7
- package/dist/src/SystemContainer.js.map +1 -1
- package/dist/src/bin/runMigrations.d.ts +2 -0
- package/dist/src/bin/runMigrations.d.ts.map +1 -0
- package/dist/src/bin/runMigrations.js +34 -0
- package/dist/src/bin/runMigrations.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/migrations/2018-08-04-102200-init.d.ts +3 -0
- package/dist/src/migrations/2018-08-04-102200-init.d.ts.map +1 -0
- package/dist/src/migrations/2018-08-04-102200-init.js +37 -0
- package/dist/src/migrations/2018-08-04-102200-init.js.map +1 -0
- package/dist/src/migrations/2018-08-05-130501-trigger-event-function.d.ts +3 -0
- package/dist/src/migrations/2018-08-05-130501-trigger-event-function.d.ts.map +1 -0
- package/dist/src/migrations/2018-08-05-130501-trigger-event-function.js +99 -0
- package/dist/src/migrations/2018-08-05-130501-trigger-event-function.js.map +1 -0
- package/dist/src/migrations/2019-01-14-143432-event-trx-id.d.ts +3 -0
- package/dist/src/migrations/2019-01-14-143432-event-trx-id.d.ts.map +1 -0
- package/dist/src/migrations/2019-01-14-143432-event-trx-id.js +17 -0
- package/dist/src/migrations/2019-01-14-143432-event-trx-id.js.map +1 -0
- package/dist/src/migrations/2019-02-04-154500-event-rebase.d.ts +3 -0
- package/dist/src/migrations/2019-02-04-154500-event-rebase.d.ts.map +1 -0
- package/dist/src/migrations/2019-02-04-154500-event-rebase.js +59 -0
- package/dist/src/migrations/2019-02-04-154500-event-rebase.js.map +1 -0
- package/dist/src/migrations/2019-03-18-104147-event-run-migration-struct.d.ts +3 -0
- package/dist/src/migrations/2019-03-18-104147-event-run-migration-struct.d.ts.map +1 -0
- package/dist/src/migrations/2019-03-18-104147-event-run-migration-struct.js +12 -0
- package/dist/src/migrations/2019-03-18-104147-event-run-migration-struct.js.map +1 -0
- package/dist/src/migrations/2019-07-29-103000-trigger-event-function-fix.d.ts +3 -0
- package/dist/src/migrations/2019-07-29-103000-trigger-event-function-fix.d.ts.map +1 -0
- package/dist/src/migrations/2019-07-29-103000-trigger-event-function-fix.js +66 -0
- package/dist/src/migrations/2019-07-29-103000-trigger-event-function-fix.js.map +1 -0
- package/dist/src/migrations/2019-11-28-100000-missing-setting-fallback.d.ts +3 -0
- package/dist/src/migrations/2019-11-28-100000-missing-setting-fallback.d.ts.map +1 -0
- package/dist/src/migrations/2019-11-28-100000-missing-setting-fallback.js +21 -0
- package/dist/src/migrations/2019-11-28-100000-missing-setting-fallback.js.map +1 -0
- package/dist/src/migrations/2021-03-23-110000-timestamp-fix.d.ts +3 -0
- package/dist/src/migrations/2021-03-23-110000-timestamp-fix.d.ts.map +1 -0
- package/dist/src/migrations/2021-03-23-110000-timestamp-fix.js +15 -0
- package/dist/src/migrations/2021-03-23-110000-timestamp-fix.js.map +1 -0
- package/dist/src/migrations/2021-05-07-155800-event-log-rework.js +2 -2
- package/dist/src/migrations/2021-12-21-155500-events-up.d.ts +3 -0
- package/dist/src/migrations/2021-12-21-155500-events-up.d.ts.map +1 -0
- package/dist/src/migrations/2021-12-21-155500-events-up.js +15 -0
- package/dist/src/migrations/2021-12-21-155500-events-up.js.map +1 -0
- package/dist/src/migrations/2022-02-08-132600-fn-search-path.d.ts +3 -0
- package/dist/src/migrations/2022-02-08-132600-fn-search-path.d.ts.map +1 -0
- package/dist/src/migrations/2022-02-08-132600-fn-search-path.js +16 -0
- package/dist/src/migrations/2022-02-08-132600-fn-search-path.js.map +1 -0
- package/dist/src/migrations/2022-02-08-140500-drop-deadcode.d.ts +3 -0
- package/dist/src/migrations/2022-02-08-140500-drop-deadcode.d.ts.map +1 -0
- package/dist/src/migrations/2022-02-08-140500-drop-deadcode.js +11 -0
- package/dist/src/migrations/2022-02-08-140500-drop-deadcode.js.map +1 -0
- package/dist/src/migrations/2022-02-08-144400-dynamic-stage-schema.d.ts +3 -0
- package/dist/src/migrations/2022-02-08-144400-dynamic-stage-schema.d.ts.map +1 -0
- package/dist/src/migrations/2022-02-08-144400-dynamic-stage-schema.js +50 -0
- package/dist/src/migrations/2022-02-08-144400-dynamic-stage-schema.js.map +1 -0
- package/dist/src/migrations/2022-10-03-110000-table-on-delete.d.ts +4 -0
- package/dist/src/migrations/2022-10-03-110000-table-on-delete.d.ts.map +1 -0
- package/dist/src/migrations/2022-10-03-110000-table-on-delete.js +61 -0
- package/dist/src/migrations/2022-10-03-110000-table-on-delete.js.map +1 -0
- package/dist/src/migrations/runner.d.ts +18 -3
- package/dist/src/migrations/runner.d.ts.map +1 -1
- package/dist/src/migrations/runner.js +69 -10
- package/dist/src/migrations/runner.js.map +1 -1
- package/dist/src/migrations/snapshot-factory.d.ts +6 -0
- package/dist/src/migrations/snapshot-factory.d.ts.map +1 -0
- package/dist/src/migrations/snapshot-factory.js +46 -0
- package/dist/src/migrations/snapshot-factory.js.map +1 -0
- package/dist/src/migrations/snapshot.d.ts +3 -0
- package/dist/src/migrations/snapshot.d.ts.map +1 -0
- package/dist/src/migrations/snapshot.js +172 -0
- package/dist/src/migrations/snapshot.js.map +1 -0
- package/dist/src/migrations/types.d.ts +2 -0
- package/dist/src/migrations/types.d.ts.map +1 -1
- package/dist/src/model/ProjectInitializer.d.ts +6 -10
- package/dist/src/model/ProjectInitializer.d.ts.map +1 -1
- package/dist/src/model/ProjectInitializer.js +11 -29
- package/dist/src/model/ProjectInitializer.js.map +1 -1
- package/dist/src/model/commands/Command.d.ts +1 -5
- package/dist/src/model/commands/Command.d.ts.map +1 -1
- package/dist/src/model/database/DatabaseContext.d.ts +5 -4
- package/dist/src/model/database/DatabaseContext.d.ts.map +1 -1
- package/dist/src/model/database/DatabaseContext.js +7 -7
- package/dist/src/model/database/DatabaseContext.js.map +1 -1
- package/dist/src/model/dtos/ExecutedMigration.d.ts +2 -1
- package/dist/src/model/dtos/ExecutedMigration.d.ts.map +1 -1
- package/dist/src/model/dtos/ExecutedMigration.js +2 -1
- package/dist/src/model/dtos/ExecutedMigration.js.map +1 -1
- package/dist/src/model/events/EventResponseBuilder.d.ts +2 -2
- package/dist/src/model/events/EventResponseBuilder.d.ts.map +1 -1
- package/dist/src/model/events/EventResponseBuilder.js +10 -10
- package/dist/src/model/events/EventResponseBuilder.js.map +1 -1
- package/dist/src/model/events/EventResponseHelper.d.ts +1 -1
- package/dist/src/model/events/EventResponseHelper.d.ts.map +1 -1
- package/dist/src/model/events/index.d.ts +1 -0
- package/dist/src/model/events/index.d.ts.map +1 -1
- package/dist/src/model/events/index.js +1 -0
- package/dist/src/model/events/index.js.map +1 -1
- package/dist/src/model/events/types.d.ts +56 -0
- package/dist/src/model/events/types.d.ts.map +1 -0
- package/dist/src/model/events/types.js +51 -0
- package/dist/src/model/events/types.js.map +1 -0
- package/dist/src/model/helpers/index.d.ts +0 -1
- package/dist/src/model/helpers/index.d.ts.map +1 -1
- package/dist/src/model/helpers/index.js +0 -1
- package/dist/src/model/helpers/index.js.map +1 -1
- package/dist/src/model/index.d.ts +1 -0
- package/dist/src/model/index.d.ts.map +1 -1
- package/dist/src/model/index.js +1 -0
- package/dist/src/model/index.js.map +1 -1
- package/dist/src/model/metadata/ResolvedDatabaseMetadata.d.ts +13 -0
- package/dist/src/model/metadata/ResolvedDatabaseMetadata.d.ts.map +1 -0
- package/dist/src/model/metadata/ResolvedDatabaseMetadata.js +42 -0
- package/dist/src/model/metadata/ResolvedDatabaseMetadata.js.map +1 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolver.d.ts +8 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolver.d.ts.map +1 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolver.js +92 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolver.js.map +1 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolverStore.d.ts +17 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolverStore.d.ts.map +1 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolverStore.js +33 -0
- package/dist/src/model/metadata/SchemaDatabaseMetadataResolverStore.js.map +1 -0
- package/dist/src/model/metadata/index.d.ts +4 -0
- package/dist/src/model/metadata/index.d.ts.map +1 -0
- package/dist/src/model/metadata/index.js +22 -0
- package/dist/src/model/metadata/index.js.map +1 -0
- package/dist/src/model/migrations/ProjectMigrator.d.ts +3 -1
- package/dist/src/model/migrations/ProjectMigrator.d.ts.map +1 -1
- package/dist/src/model/migrations/ProjectMigrator.js +14 -6
- package/dist/src/model/migrations/ProjectMigrator.js.map +1 -1
- package/dist/src/model/migrations/SchemaVersionBuilder.d.ts +20 -0
- package/dist/src/model/migrations/SchemaVersionBuilder.d.ts.map +1 -1
- package/dist/src/model/migrations/SchemaVersionBuilder.js +5 -4
- package/dist/src/model/migrations/SchemaVersionBuilder.js.map +1 -1
- package/dist/src/model/queries/events/EventQueryHelpers.d.ts +2 -2
- package/dist/src/model/queries/events/EventQueryHelpers.d.ts.map +1 -1
- package/dist/src/model/queries/events/EventQueryHelpers.js +7 -7
- package/dist/src/model/queries/events/EventQueryHelpers.js.map +1 -1
- package/dist/src/model/queries/events/EventsQuery.d.ts +2 -2
- package/dist/src/model/queries/events/EventsQuery.d.ts.map +1 -1
- package/dist/src/model/queries/events/EventsQuery.js.map +1 -1
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.d.ts +1 -0
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.d.ts.map +1 -1
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.js +2 -1
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/cases/migrations/2020-05-06-150000-composed-primary.test.js +2 -0
- package/dist/tests/cases/migrations/2020-05-06-150000-composed-primary.test.js.map +1 -1
- package/dist/tests/cases/migrations/2020-06-01-103000-event-trigger-perf.test.js +2 -0
- package/dist/tests/cases/migrations/2020-06-01-103000-event-trigger-perf.test.js.map +1 -1
- package/dist/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.d.ts +2 -0
- package/dist/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.d.ts.map +1 -0
- package/dist/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.js +43 -0
- package/dist/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.js.map +1 -0
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -15
- package/src/SystemContainer.ts +8 -18
- package/src/bin/runMigrations.ts +42 -0
- package/src/index.ts +2 -2
- package/src/migrations/{2018-08-04-102200-init.sql → 2018-08-04-102200-init.ts} +8 -0
- package/src/migrations/{2018-08-05-130501-trigger-event-function.sql → 2018-08-05-130501-trigger-event-function.ts} +8 -0
- package/src/migrations/{2019-01-14-143432-event-trx-id.sql → 2019-01-14-143432-event-trx-id.ts} +8 -0
- package/src/migrations/{2019-02-04-154500-event-rebase.sql → 2019-02-04-154500-event-rebase.ts} +9 -0
- package/src/migrations/2019-03-18-104147-event-run-migration-struct.ts +11 -0
- package/src/migrations/{2019-07-29-103000-trigger-event-function-fix.sql → 2019-07-29-103000-trigger-event-function-fix.ts} +8 -0
- package/src/migrations/{2019-11-28-100000-missing-setting-fallback.sql → 2019-11-28-100000-missing-setting-fallback.ts} +8 -0
- package/src/migrations/{2021-03-23-110000-timestamp-fix.sql → 2021-03-23-110000-timestamp-fix.ts} +8 -0
- package/src/migrations/2021-05-07-155800-event-log-rework.ts +2 -2
- package/src/migrations/{2021-12-21-155500-events-up.sql → 2021-12-21-155500-events-up.ts} +8 -0
- package/src/migrations/{2022-02-08-132600-fn-search-path.sql → 2022-02-08-132600-fn-search-path.ts} +8 -0
- package/src/migrations/2022-02-08-140500-drop-deadcode.ts +10 -0
- package/src/migrations/{2022-02-08-144400-dynamic-stage-schema.sql → 2022-02-08-144400-dynamic-stage-schema.ts} +8 -0
- package/src/migrations/2022-10-03-110000-table-on-delete.ts +65 -0
- package/src/migrations/runner.ts +85 -15
- package/src/migrations/snapshot-factory.ts +45 -0
- package/src/migrations/snapshot-template-end.txt +1 -0
- package/src/migrations/snapshot-template-start.txt +2 -0
- package/src/migrations/snapshot.ts +169 -0
- package/src/migrations/types.ts +2 -0
- package/src/model/ProjectInitializer.ts +15 -50
- package/src/model/commands/Command.ts +1 -1
- package/src/model/database/DatabaseContext.ts +12 -8
- package/src/model/dtos/ExecutedMigration.ts +1 -0
- package/src/model/events/EventResponseBuilder.ts +3 -3
- package/src/model/events/EventResponseHelper.ts +1 -1
- package/src/model/events/index.ts +1 -0
- package/src/model/events/types.ts +63 -0
- package/src/model/helpers/index.ts +0 -1
- package/src/model/index.ts +1 -0
- package/src/model/metadata/ResolvedDatabaseMetadata.ts +54 -0
- package/src/model/metadata/SchemaDatabaseMetadataResolver.ts +136 -0
- package/src/model/metadata/SchemaDatabaseMetadataResolverStore.ts +41 -0
- package/src/model/metadata/index.ts +3 -0
- package/src/model/migrations/ProjectMigrator.ts +19 -5
- package/src/model/migrations/SchemaVersionBuilder.ts +3 -2
- package/src/model/queries/events/EventQueryHelpers.ts +2 -2
- package/src/model/queries/events/EventsQuery.ts +2 -2
- package/src/model/queries/migrations/ExecutedMigrationsQueryHelper.ts +3 -1
- package/src/tsconfig.json +10 -33
- package/tests/cases/migrations/2020-05-06-150000-composed-primary.test.ts +2 -0
- package/tests/cases/migrations/2020-06-01-103000-event-trigger-perf.test.ts +2 -0
- package/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.ts +45 -0
- package/tests/tsconfig.json +2 -6
- package/tsconfig.json +2 -6
- package/dist/src/model/helpers/SystemVariablesSetupHelper.d.ts +0 -5
- package/dist/src/model/helpers/SystemVariablesSetupHelper.d.ts.map +0 -1
- package/dist/src/model/helpers/SystemVariablesSetupHelper.js +0 -15
- package/dist/src/model/helpers/SystemVariablesSetupHelper.js.map +0 -1
- package/dist/tests/cases/integration/systemApi/projectInitializer.test.d.ts +0 -2
- package/dist/tests/cases/integration/systemApi/projectInitializer.test.d.ts.map +0 -1
- package/dist/tests/cases/integration/systemApi/projectInitializer.test.js +0 -26
- package/dist/tests/cases/integration/systemApi/projectInitializer.test.js.map +0 -1
- package/src/migrations/2019-03-18-104147-event-run-migration-struct.sql +0 -3
- package/src/migrations/2022-02-08-140500-drop-deadcode.sql +0 -2
- package/src/model/helpers/SystemVariablesSetupHelper.ts +0 -14
- package/tests/cases/integration/systemApi/projectInitializer.test.ts +0 -31
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { snapshotMigration } from './snapshot-factory'
|
|
2
|
+
export default snapshotMigration(({ randomUuidFn }) => `
|
|
3
|
+
CREATE DOMAIN "event_data_type" AS "text"
|
|
4
|
+
CONSTRAINT "event_data_type_check" CHECK ((VALUE = ANY (ARRAY['create'::"text", 'update'::"text", 'delete'::"text"])));
|
|
5
|
+
CREATE DOMAIN "event_type" AS "text"
|
|
6
|
+
CONSTRAINT "event_type_check" CHECK ((VALUE = ANY (ARRAY['init'::"text", 'create'::"text", 'update'::"text", 'delete'::"text", 'run_migration'::"text"])));
|
|
7
|
+
CREATE FUNCTION "trigger_event"() RETURNS "trigger"
|
|
8
|
+
LANGUAGE "plpgsql"
|
|
9
|
+
AS $$
|
|
10
|
+
DECLARE new_event_type TEXT;
|
|
11
|
+
DECLARE table_name TEXT;
|
|
12
|
+
DECLARE values JSONB;
|
|
13
|
+
DECLARE update_diff JSONB;
|
|
14
|
+
DECLARE primary_values JSONB;
|
|
15
|
+
DECLARE old_row JSONB;
|
|
16
|
+
DECLARE new_row JSONB;
|
|
17
|
+
BEGIN
|
|
18
|
+
old_row := CASE WHEN TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN TO_JSONB(old) ELSE NULL END;
|
|
19
|
+
new_row := CASE WHEN TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN TO_JSONB(new) ELSE NULL END;
|
|
20
|
+
primary_values := (
|
|
21
|
+
SELECT JSONB_AGG(value ORDER BY array_position(TG_ARGV, key))
|
|
22
|
+
FROM JSONB_EACH(COALESCE(old_row, new_row))
|
|
23
|
+
WHERE key = ANY (TG_ARGV)
|
|
24
|
+
);
|
|
25
|
+
table_name := tg_table_name;
|
|
26
|
+
CASE TG_OP
|
|
27
|
+
WHEN 'INSERT' THEN BEGIN
|
|
28
|
+
new_event_type := 'create';
|
|
29
|
+
values := COALESCE(
|
|
30
|
+
(
|
|
31
|
+
SELECT JSONB_OBJECT_AGG(key, value)
|
|
32
|
+
FROM JSONB_EACH(new_row)
|
|
33
|
+
WHERE NOT (key = ANY (TG_ARGV))
|
|
34
|
+
),
|
|
35
|
+
JSONB_BUILD_OBJECT()
|
|
36
|
+
);
|
|
37
|
+
END;
|
|
38
|
+
WHEN 'UPDATE' THEN BEGIN
|
|
39
|
+
update_diff := (
|
|
40
|
+
SELECT JSONB_OBJECT_AGG(COALESCE(n.key, o.key), n.value)
|
|
41
|
+
FROM JSONB_EACH(old_row) o
|
|
42
|
+
FULL OUTER JOIN JSONB_EACH(new_row) n ON n.key = o.key
|
|
43
|
+
WHERE n.value IS DISTINCT FROM o.value
|
|
44
|
+
);
|
|
45
|
+
IF (SELECT COUNT(*) FROM JSONB_OBJECT_KEYS(update_diff)) = 0 THEN
|
|
46
|
+
RETURN NULL;
|
|
47
|
+
END IF;
|
|
48
|
+
new_event_type := 'update';
|
|
49
|
+
values := update_diff;
|
|
50
|
+
END;
|
|
51
|
+
WHEN 'DELETE' THEN BEGIN
|
|
52
|
+
new_event_type := 'delete';
|
|
53
|
+
END;
|
|
54
|
+
ELSE RAISE
|
|
55
|
+
EXCEPTION 'Unknown TG_OP value %', TG_OP;
|
|
56
|
+
END CASE;
|
|
57
|
+
CASE WHEN NULLIF(CURRENT_SETTING('system.schema_id', true), '') IS NULL THEN
|
|
58
|
+
PERFORM SET_CONFIG('system.schema_id', (select MAX("id")::TEXT from schema_migration), TRUE);
|
|
59
|
+
ELSE
|
|
60
|
+
-- do nothing
|
|
61
|
+
END CASE;
|
|
62
|
+
INSERT INTO "event_data" ("id", "type", "table_name", "row_ids", "values", "created_at", schema_id, identity_id)
|
|
63
|
+
VALUES (
|
|
64
|
+
"${randomUuidFn}"(),
|
|
65
|
+
new_event_type,
|
|
66
|
+
table_name,
|
|
67
|
+
primary_values,
|
|
68
|
+
values,
|
|
69
|
+
CLOCK_TIMESTAMP(),
|
|
70
|
+
CURRENT_SETTING('system.schema_id')::INT,
|
|
71
|
+
COALESCE(
|
|
72
|
+
CURRENT_SETTING('tenant.identity_id'::TEXT, TRUE)::UUID,
|
|
73
|
+
'00000000-0000-0000-0000-000000000000'::UUID)
|
|
74
|
+
);
|
|
75
|
+
RETURN NULL;
|
|
76
|
+
END;
|
|
77
|
+
$$;
|
|
78
|
+
CREATE FUNCTION "trigger_event_commit"() RETURNS "trigger"
|
|
79
|
+
LANGUAGE "plpgsql"
|
|
80
|
+
AS $$
|
|
81
|
+
BEGIN
|
|
82
|
+
PERFORM PG_ADVISORY_XACT_LOCK(5218230332970456493); -- well-known random number
|
|
83
|
+
CASE WHEN
|
|
84
|
+
NULLIF(CURRENT_SETTING('system.transaction_id', TRUE), '') IS NOT NULL
|
|
85
|
+
AND NULLIF(CURRENT_SETTING('system.transaction_inserted', TRUE), '') IS NULL
|
|
86
|
+
THEN
|
|
87
|
+
INSERT INTO stage_transaction(transaction_id, stage_id, applied_at)
|
|
88
|
+
VALUES (CURRENT_SETTING('system.transaction_id')::UUID,
|
|
89
|
+
(
|
|
90
|
+
SELECT id
|
|
91
|
+
FROM stage
|
|
92
|
+
WHERE schema = TG_TABLE_SCHEMA
|
|
93
|
+
),
|
|
94
|
+
CLOCK_TIMESTAMP())
|
|
95
|
+
ON CONFLICT DO NOTHING;
|
|
96
|
+
PERFORM SET_CONFIG('system.transaction_inserted', '1', TRUE);
|
|
97
|
+
ELSE
|
|
98
|
+
-- do nothing
|
|
99
|
+
END CASE;
|
|
100
|
+
RETURN NULL;
|
|
101
|
+
END
|
|
102
|
+
$$;
|
|
103
|
+
CREATE TABLE "event_data" (
|
|
104
|
+
"id" "uuid" NOT NULL,
|
|
105
|
+
"type" "event_data_type" NOT NULL,
|
|
106
|
+
"table_name" "text" NOT NULL,
|
|
107
|
+
"row_ids" "jsonb" NOT NULL,
|
|
108
|
+
"values" "jsonb",
|
|
109
|
+
"created_at" timestamp with time zone DEFAULT "clock_timestamp"() NOT NULL,
|
|
110
|
+
"schema_id" integer NOT NULL,
|
|
111
|
+
"identity_id" "uuid" NOT NULL,
|
|
112
|
+
"transaction_id" "uuid" DEFAULT COALESCE((NULLIF("current_setting"('system.transaction_id'::"text", true), ''::"text"))::"uuid", ("set_config"('system.transaction_id'::"text", ("${randomUuidFn}"())::"text", true))::"uuid") NOT NULL
|
|
113
|
+
);
|
|
114
|
+
CREATE TABLE "schema_migration" (
|
|
115
|
+
"id" integer NOT NULL,
|
|
116
|
+
"version" character varying(20) NOT NULL,
|
|
117
|
+
"name" character varying(255) NOT NULL,
|
|
118
|
+
"migration" "json" NOT NULL,
|
|
119
|
+
"checksum" character(32) NOT NULL,
|
|
120
|
+
"executed_at" timestamp with time zone DEFAULT "now"() NOT NULL
|
|
121
|
+
);
|
|
122
|
+
CREATE SEQUENCE "schema_migration_id_seq"
|
|
123
|
+
AS integer
|
|
124
|
+
START WITH 1
|
|
125
|
+
INCREMENT BY 1
|
|
126
|
+
NO MINVALUE
|
|
127
|
+
NO MAXVALUE
|
|
128
|
+
CACHE 1;
|
|
129
|
+
ALTER SEQUENCE "schema_migration_id_seq" OWNED BY "schema_migration"."id";
|
|
130
|
+
CREATE TABLE "stage" (
|
|
131
|
+
"id" "uuid" NOT NULL,
|
|
132
|
+
"name" "text" NOT NULL,
|
|
133
|
+
"slug" "text" NOT NULL,
|
|
134
|
+
"schema" "text" NOT NULL
|
|
135
|
+
);
|
|
136
|
+
CREATE TABLE "stage_transaction" (
|
|
137
|
+
"transaction_id" "uuid" NOT NULL,
|
|
138
|
+
"stage_id" "uuid" NOT NULL,
|
|
139
|
+
"applied_at" timestamp with time zone NOT NULL
|
|
140
|
+
);
|
|
141
|
+
ALTER TABLE ONLY "schema_migration" ALTER COLUMN "id" SET DEFAULT "nextval"('"schema_migration_id_seq"'::"regclass");
|
|
142
|
+
ALTER TABLE ONLY "event_data"
|
|
143
|
+
ADD CONSTRAINT "event_data_pkey" PRIMARY KEY ("id");
|
|
144
|
+
ALTER TABLE ONLY "schema_migration"
|
|
145
|
+
ADD CONSTRAINT "schema_migration_name_key" UNIQUE ("name");
|
|
146
|
+
ALTER TABLE ONLY "schema_migration"
|
|
147
|
+
ADD CONSTRAINT "schema_migration_pkey" PRIMARY KEY ("id");
|
|
148
|
+
ALTER TABLE ONLY "schema_migration"
|
|
149
|
+
ADD CONSTRAINT "schema_migration_version_key" UNIQUE ("version");
|
|
150
|
+
ALTER TABLE ONLY "stage"
|
|
151
|
+
ADD CONSTRAINT "stage_pkey" PRIMARY KEY ("id");
|
|
152
|
+
ALTER TABLE ONLY "stage"
|
|
153
|
+
ADD CONSTRAINT "stage_schema_key" UNIQUE ("schema");
|
|
154
|
+
ALTER TABLE ONLY "stage"
|
|
155
|
+
ADD CONSTRAINT "stage_slug_key" UNIQUE ("slug");
|
|
156
|
+
ALTER TABLE ONLY "stage_transaction"
|
|
157
|
+
ADD CONSTRAINT "stage_transaction_pkey" PRIMARY KEY ("transaction_id", "stage_id");
|
|
158
|
+
CREATE INDEX "event_data_created" ON "event_data" USING "btree" ("created_at");
|
|
159
|
+
CREATE INDEX "system_event2_table_name" ON "event_data" USING "btree" ("table_name");
|
|
160
|
+
CREATE INDEX "system_event2_transaction" ON "event_data" USING "btree" ("transaction_id");
|
|
161
|
+
CREATE INDEX "system_event2_type" ON "event_data" USING "btree" ("type");
|
|
162
|
+
CREATE INDEX "system_event_row_id" ON "event_data" USING "btree" ("row_ids");
|
|
163
|
+
CREATE INDEX "system_schema_migration_version" ON "schema_migration" USING "btree" ("version");
|
|
164
|
+
CREATE INDEX "transaction_applied" ON "stage_transaction" USING "btree" ("applied_at");
|
|
165
|
+
ALTER TABLE ONLY "event_data"
|
|
166
|
+
ADD CONSTRAINT "event_data_schema_id_fkey" FOREIGN KEY ("schema_id") REFERENCES "schema_migration"("id");
|
|
167
|
+
ALTER TABLE ONLY "stage_transaction"
|
|
168
|
+
ADD CONSTRAINT "stage_transaction_stage_id_fkey" FOREIGN KEY ("stage_id") REFERENCES "stage"("id") ON DELETE CASCADE;
|
|
169
|
+
`)
|
package/src/migrations/types.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Schema } from '@contember/schema'
|
|
2
2
|
import { ProjectConfig } from '../types'
|
|
3
3
|
import { Connection } from '@contember/database'
|
|
4
|
+
import { SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
4
5
|
|
|
5
6
|
export interface SystemMigrationArgs {
|
|
6
7
|
schemaResolver: (connection: Connection.ConnectionLike) => Promise<Schema>
|
|
8
|
+
databaseMetadataResolver: (connection: Connection.ConnectionLike, schema: string) => Promise<SchemaDatabaseMetadata>
|
|
7
9
|
project: ProjectConfig
|
|
8
10
|
}
|
|
@@ -1,63 +1,28 @@
|
|
|
1
1
|
import { ProjectConfig } from '../types'
|
|
2
|
-
import { ProjectMigrator, SchemaVersionBuilder } from './migrations'
|
|
3
2
|
import { StageCreator } from './stages'
|
|
4
3
|
import { DatabaseContext, DatabaseContextFactory } from './database'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
Client,
|
|
8
|
-
Connection,
|
|
9
|
-
createDatabaseIfNotExists,
|
|
10
|
-
DatabaseConfig,
|
|
11
|
-
EventManager,
|
|
12
|
-
retryTransaction,
|
|
13
|
-
} from '@contember/database'
|
|
4
|
+
import { Connection, retryTransaction } from '@contember/database'
|
|
14
5
|
import { FingerCrossedLoggerHandler, Logger } from '@contember/logger'
|
|
6
|
+
import { SystemMigrationsRunner } from '../migrations'
|
|
15
7
|
|
|
16
8
|
export class ProjectInitializer {
|
|
17
9
|
constructor(
|
|
18
|
-
private readonly projectMigrator: ProjectMigrator,
|
|
19
10
|
private readonly stageCreator: StageCreator,
|
|
20
|
-
private readonly
|
|
21
|
-
private readonly
|
|
11
|
+
private readonly migrationsRunner: SystemMigrationsRunner,
|
|
12
|
+
private readonly databaseContextFactory: DatabaseContextFactory,
|
|
13
|
+
private readonly project: ProjectConfig,
|
|
22
14
|
) {}
|
|
23
15
|
|
|
24
|
-
public async initialize(
|
|
25
|
-
databaseContextFactory: DatabaseContextFactory,
|
|
26
|
-
project: ProjectConfig & { db?: DatabaseConfig },
|
|
27
|
-
logger: Logger,
|
|
28
|
-
) {
|
|
29
|
-
const dbContext = databaseContextFactory.create()
|
|
16
|
+
public async initialize(logger: Logger) {
|
|
30
17
|
return await logger.scope(async initLogger => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
await singleConnection.scope(async connection => {
|
|
38
|
-
const systemSchema = dbContext.client.schema
|
|
39
|
-
|
|
40
|
-
const schemaResolver = (connection: Connection.ConnectionLike) => {
|
|
41
|
-
const dbContextMigrations = databaseContextFactory
|
|
42
|
-
.withClient(new Client(connection, systemSchema, { module: 'system' }))
|
|
43
|
-
.create()
|
|
44
|
-
return this.schemaVersionBuilder.buildSchema(dbContextMigrations)
|
|
45
|
-
}
|
|
46
|
-
await this.systemDbMigrationsRunnerFactory(connection, systemSchema).migrate(
|
|
47
|
-
message => initLogger.warn(message),
|
|
48
|
-
{
|
|
49
|
-
schemaResolver,
|
|
50
|
-
project,
|
|
51
|
-
},
|
|
52
|
-
)
|
|
53
|
-
})
|
|
54
|
-
await singleConnection.end()
|
|
55
|
-
}
|
|
56
|
-
await retryTransaction(() =>
|
|
57
|
-
dbContext.transaction(async trx => {
|
|
58
|
-
await this.initStages(trx, project, initLogger)
|
|
18
|
+
initLogger.debug('Executing system schema migrations')
|
|
19
|
+
await this.migrationsRunner.run(initLogger)
|
|
20
|
+
const dbContext = this.databaseContextFactory.create()
|
|
21
|
+
await retryTransaction(
|
|
22
|
+
() => dbContext.transaction(async trx => {
|
|
23
|
+
await this.initStages(trx, logger)
|
|
59
24
|
}),
|
|
60
|
-
|
|
25
|
+
message => initLogger.warn(message),
|
|
61
26
|
)
|
|
62
27
|
if (dbContext.client.connection instanceof Connection) {
|
|
63
28
|
await dbContext.client.connection.clearPool()
|
|
@@ -65,9 +30,9 @@ export class ProjectInitializer {
|
|
|
65
30
|
}, {}, { handler: FingerCrossedLoggerHandler.factory() })
|
|
66
31
|
}
|
|
67
32
|
|
|
68
|
-
private async initStages(db: DatabaseContext<Connection.TransactionLike>,
|
|
33
|
+
private async initStages(db: DatabaseContext<Connection.TransactionLike>, logger: Logger) {
|
|
69
34
|
logger.debug(`Creating stages`)
|
|
70
|
-
for (const stage of project.stages) {
|
|
35
|
+
for (const stage of this.project.stages) {
|
|
71
36
|
const created = await this.stageCreator.createStage(db, stage)
|
|
72
37
|
if (created) {
|
|
73
38
|
logger.warn(`Created stage ${stage.slug}`, { stage: stage.slug })
|
|
@@ -3,7 +3,7 @@ import { CommandBus } from './CommandBus'
|
|
|
3
3
|
import { Providers } from '../../utils'
|
|
4
4
|
|
|
5
5
|
interface Command<Result> {
|
|
6
|
-
execute(args:
|
|
6
|
+
execute(args: Command.Args): Promise<Result>
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
namespace Command {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client, Connection, DatabaseQueryable
|
|
1
|
+
import { Client, Connection, DatabaseQueryable } from '@contember/database'
|
|
2
2
|
import { QueryHandler } from '@contember/queryable'
|
|
3
3
|
import { UuidProvider } from '../../utils'
|
|
4
4
|
import { CommandBus } from '../commands'
|
|
@@ -7,19 +7,21 @@ export interface DatabaseContext<ConnectionType extends Connection.ConnectionLik
|
|
|
7
7
|
client: Client<ConnectionType>
|
|
8
8
|
queryHandler: QueryHandler<DatabaseQueryable>
|
|
9
9
|
transaction: <T>(cb: (db: DatabaseContext<Connection.TransactionLike>) => Promise<T> | T) => Promise<T>
|
|
10
|
+
scope: <T>(cb: (db: DatabaseContext<ConnectionType & Connection.AcquiredConnectionLike>) => Promise<T> | T) => Promise<T>
|
|
10
11
|
locked: <T>(lock: number, cb: (db: DatabaseContext<Connection.ConnectionLike>) => Promise<T> | T) => Promise<T>
|
|
11
12
|
commandBus: CommandBus
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export class DatabaseContextFactory {
|
|
15
|
-
constructor(
|
|
16
|
+
constructor(
|
|
17
|
+
public readonly schemaName: string,
|
|
18
|
+
private readonly connection: Connection,
|
|
19
|
+
private readonly providers: UuidProvider,
|
|
20
|
+
) {}
|
|
16
21
|
|
|
17
|
-
public create(): DatabaseContext {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
public withClient(client: Client) {
|
|
22
|
-
return new DatabaseContextFactory(client, this.providers)
|
|
22
|
+
public create(connection?: Connection.ConnectionLike): DatabaseContext {
|
|
23
|
+
const client = new Client(connection ?? this.connection, this.schemaName, { module: 'system' })
|
|
24
|
+
return createDatabaseContext(client, this.providers)
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -34,6 +36,8 @@ const createDatabaseContext = <ConnectionType extends Connection.ConnectionLike
|
|
|
34
36
|
await client.query(Connection.REPEATABLE_READ)
|
|
35
37
|
return cb(createDatabaseContext(client, providers))
|
|
36
38
|
}),
|
|
39
|
+
scope: cb =>
|
|
40
|
+
client.scope(client => cb(createDatabaseContext(client, providers))),
|
|
37
41
|
commandBus: new CommandBus(client, providers),
|
|
38
42
|
locked: (lock, cb) => client.locked(lock, client => cb(createDatabaseContext(client, providers))),
|
|
39
43
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ContentEvent, EventType } from './types'
|
|
2
2
|
import { CreateEvent, DeleteEvent, EventType as ApiEventType, UpdateEvent } from '../../schema'
|
|
3
3
|
import { assertNever } from '../../utils'
|
|
4
4
|
import { IdentityFetcher } from '../dependencies'
|
|
@@ -8,7 +8,7 @@ import { appendCreateSpecificData, appendDeleteSpecificData, appendUpdateSpecifi
|
|
|
8
8
|
export class EventResponseBuilder {
|
|
9
9
|
constructor(private readonly identityFetcher: IdentityFetcher) {}
|
|
10
10
|
|
|
11
|
-
public async buildResponse(events:
|
|
11
|
+
public async buildResponse(events: ContentEvent[]): Promise<(CreateEvent | DeleteEvent | UpdateEvent)[]> {
|
|
12
12
|
const apiEventTypeMapping = {
|
|
13
13
|
[EventType.create]: ApiEventType.Create,
|
|
14
14
|
[EventType.update]: ApiEventType.Update,
|
|
@@ -41,7 +41,7 @@ export class EventResponseBuilder {
|
|
|
41
41
|
})
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
private formatDescription(event:
|
|
44
|
+
private formatDescription(event: ContentEvent): string {
|
|
45
45
|
switch (event.type) {
|
|
46
46
|
case EventType.create:
|
|
47
47
|
return `Creating ${event.tableName}#${event.rowId.join(';')}`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CreateEvent, DeleteEvent, UpdateEvent } from './types'
|
|
2
2
|
|
|
3
3
|
export const appendUpdateSpecificData = <T>(commonData: T, event: UpdateEvent) => ({
|
|
4
4
|
...commonData,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export enum EventType {
|
|
2
|
+
delete = 'delete',
|
|
3
|
+
update = 'update',
|
|
4
|
+
create = 'create',
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type ContentEvent = UpdateEvent | CreateEvent | DeleteEvent
|
|
8
|
+
|
|
9
|
+
export interface Event {
|
|
10
|
+
readonly type: EventType
|
|
11
|
+
readonly id: string
|
|
12
|
+
readonly createdAt: Date
|
|
13
|
+
readonly transactionId: string
|
|
14
|
+
// readonly errors?: string[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class UpdateEvent implements Event {
|
|
18
|
+
public readonly type = EventType.update
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
public readonly id: string,
|
|
22
|
+
public readonly createdAt: Date,
|
|
23
|
+
public readonly appliedAt: Date,
|
|
24
|
+
public readonly identityId: string,
|
|
25
|
+
public readonly transactionId: string,
|
|
26
|
+
public readonly rowId: string[],
|
|
27
|
+
public readonly tableName: string,
|
|
28
|
+
public readonly values: { [column: string]: any },
|
|
29
|
+
) {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class CreateEvent implements Event {
|
|
34
|
+
public readonly type = EventType.create
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
public readonly id: string,
|
|
38
|
+
public readonly createdAt: Date,
|
|
39
|
+
public readonly appliedAt: Date,
|
|
40
|
+
public readonly identityId: string,
|
|
41
|
+
public readonly transactionId: string,
|
|
42
|
+
public readonly rowId: string[],
|
|
43
|
+
public readonly tableName: string,
|
|
44
|
+
public readonly values: { [column: string]: any },
|
|
45
|
+
) {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class DeleteEvent implements Event {
|
|
50
|
+
public readonly type = EventType.delete
|
|
51
|
+
|
|
52
|
+
constructor(
|
|
53
|
+
public readonly id: string,
|
|
54
|
+
public readonly createdAt: Date,
|
|
55
|
+
public readonly appliedAt: Date,
|
|
56
|
+
public readonly identityId: string,
|
|
57
|
+
public readonly transactionId: string,
|
|
58
|
+
public readonly rowId: string[],
|
|
59
|
+
public readonly tableName: string,
|
|
60
|
+
) {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
package/src/model/index.ts
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ForeignKeyConstraintMetadata,
|
|
3
|
+
GetForeignKeyConstraintNameArgs,
|
|
4
|
+
GetIndexNameArgs,
|
|
5
|
+
GetUniqueConstraintNameArgs,
|
|
6
|
+
IndexMetadata,
|
|
7
|
+
SchemaDatabaseMetadata,
|
|
8
|
+
UniqueConstraintMetadata,
|
|
9
|
+
} from '@contember/schema-utils'
|
|
10
|
+
|
|
11
|
+
const stringArrayEquals = (colA: string[], colB: string[]) => {
|
|
12
|
+
return colA.length === colB.length
|
|
13
|
+
&& colA.every((it, index) => it === colB[index])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class ResolvedDatabaseMetadata implements SchemaDatabaseMetadata {
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly foreignKeys: ForeignKeyConstraintMetadata[],
|
|
19
|
+
private readonly uniqueConstraints: UniqueConstraintMetadata[],
|
|
20
|
+
private readonly indexes: IndexMetadata[],
|
|
21
|
+
) {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getUniqueConstraint(tableName: string, constraintName: string): UniqueConstraintMetadata | null {
|
|
25
|
+
return this.uniqueConstraints.find(it => it.tableName === tableName && it.constraintName === constraintName) ?? null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getForeignKeyConstraint(tableName: string, constraintName: string): ForeignKeyConstraintMetadata | null {
|
|
29
|
+
return this.foreignKeys.find(it => it.fromTable === tableName && it.constraintName === constraintName) ?? null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getForeignKeyConstraintNames({ fromTable, fromColumn, toTable, toColumn }: GetForeignKeyConstraintNameArgs): string[] {
|
|
33
|
+
return this.foreignKeys
|
|
34
|
+
.filter(it =>
|
|
35
|
+
it.fromTable === fromTable
|
|
36
|
+
&& it.toTable === toTable
|
|
37
|
+
&& it.fromColumn === fromColumn
|
|
38
|
+
&& it.toColumn === toColumn,
|
|
39
|
+
)
|
|
40
|
+
.map(it => it.constraintName)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getUniqueConstraintNames({ tableName, columnNames }: GetUniqueConstraintNameArgs): string[] {
|
|
44
|
+
return this.uniqueConstraints
|
|
45
|
+
.filter(it => it.tableName === tableName && stringArrayEquals(columnNames, it.columnNames))
|
|
46
|
+
.map(it => it.constraintName)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getIndexNames({ tableName, columnNames }: GetIndexNameArgs): string[] {
|
|
50
|
+
return this.indexes
|
|
51
|
+
.filter(it => it.tableName === tableName && stringArrayEquals(columnNames, it.columnNames))
|
|
52
|
+
.map(it => it.indexName)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ForeignKeyConstraintMetadata,
|
|
3
|
+
IndexMetadata,
|
|
4
|
+
SchemaDatabaseMetadata,
|
|
5
|
+
UniqueConstraintMetadata,
|
|
6
|
+
} from '@contember/schema-utils'
|
|
7
|
+
import { DatabaseContext } from '../database'
|
|
8
|
+
import { Connection } from '@contember/database'
|
|
9
|
+
import { ResolvedDatabaseMetadata } from './ResolvedDatabaseMetadata'
|
|
10
|
+
|
|
11
|
+
export class SchemaDatabaseMetadataResolver {
|
|
12
|
+
async resolveMetadata(db: DatabaseContext, contentSchema: string): Promise<SchemaDatabaseMetadata> {
|
|
13
|
+
const constraintRows = await this.fetchConstraints(db.client, contentSchema)
|
|
14
|
+
const indexRows = await this.fetchIndexes(db.client, contentSchema)
|
|
15
|
+
|
|
16
|
+
const fkConstraints = constraintRows
|
|
17
|
+
.filter((it): it is ForeignKeyConstraintsRow => it.type === ConstraintTypes.foreignKey)
|
|
18
|
+
.map((it): ForeignKeyConstraintMetadata => ({
|
|
19
|
+
constraintName: it.constraint_name,
|
|
20
|
+
fromColumn: it.columns[0],
|
|
21
|
+
toColumn: it.target_columns[0],
|
|
22
|
+
fromTable: it.table_name,
|
|
23
|
+
toTable: it.target_table,
|
|
24
|
+
}))
|
|
25
|
+
const uniqueConstraints = constraintRows
|
|
26
|
+
.filter((it): it is UniqueConstraintsRow => it.type === ConstraintTypes.unique)
|
|
27
|
+
.map((it): UniqueConstraintMetadata => ({
|
|
28
|
+
constraintName: it.constraint_name,
|
|
29
|
+
tableName: it.table_name,
|
|
30
|
+
columnNames: it.columns,
|
|
31
|
+
}))
|
|
32
|
+
const indexes = indexRows
|
|
33
|
+
.map((it): IndexMetadata => ({
|
|
34
|
+
indexName: it.index_name,
|
|
35
|
+
tableName: it.table_name,
|
|
36
|
+
columnNames: it.columns,
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
return new ResolvedDatabaseMetadata(
|
|
40
|
+
fkConstraints,
|
|
41
|
+
uniqueConstraints,
|
|
42
|
+
indexes,
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private async fetchConstraints(connection: Connection.Queryable, schema: string): Promise<ConstraintsRow[]> {
|
|
47
|
+
return (await connection.query<ConstraintsRow>(`
|
|
48
|
+
SELECT
|
|
49
|
+
MAX(pg_constraint.conname) AS constraint_name,
|
|
50
|
+
MAX(pg_class.relname) AS table_name,
|
|
51
|
+
JSONB_AGG(DISTINCT pg_attribute.attname) AS columns,
|
|
52
|
+
MAX(pg_constraint.contype) AS type,
|
|
53
|
+
MAX(pg_constraint_table.relname) AS target_table,
|
|
54
|
+
JSONB_AGG(DISTINCT pg_attribute_target.attname)
|
|
55
|
+
FILTER ( WHERE pg_attribute_target.attname IS NOT NULL) AS target_columns,
|
|
56
|
+
BOOL_OR(condeferrable),
|
|
57
|
+
BOOL_OR(condeferred)
|
|
58
|
+
FROM pg_constraint
|
|
59
|
+
JOIN pg_class
|
|
60
|
+
ON pg_constraint.conrelid = pg_class.oid
|
|
61
|
+
JOIN pg_namespace
|
|
62
|
+
ON pg_class.relnamespace = pg_namespace.oid
|
|
63
|
+
JOIN pg_attribute
|
|
64
|
+
ON pg_attribute.attrelid = pg_class.oid AND pg_attribute.attnum = ANY (pg_constraint.conkey)
|
|
65
|
+
LEFT JOIN pg_attribute pg_attribute_target
|
|
66
|
+
ON pg_constraint.confrelid = pg_attribute_target.attrelid AND
|
|
67
|
+
pg_attribute_target.attnum = ANY (pg_constraint.confkey)
|
|
68
|
+
LEFT JOIN pg_class pg_constraint_table
|
|
69
|
+
ON pg_constraint_table.oid = pg_constraint.confrelid
|
|
70
|
+
WHERE pg_namespace.nspname = ?
|
|
71
|
+
AND pg_constraint.contype = ANY (ARRAY ['f', 'u'])
|
|
72
|
+
GROUP BY pg_constraint.oid
|
|
73
|
+
`, [schema]))
|
|
74
|
+
.rows
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private async fetchIndexes(connection: Connection.Queryable, schema: string): Promise<IndexRow[]> {
|
|
78
|
+
return (await connection.query<IndexRow>(`
|
|
79
|
+
SELECT
|
|
80
|
+
MAX(idx_class.relname) AS index_name,
|
|
81
|
+
MAX(table_class.relname) AS table_name,
|
|
82
|
+
JSONB_AGG(DISTINCT pg_attribute.attname) AS columns
|
|
83
|
+
FROM pg_index
|
|
84
|
+
JOIN pg_class AS table_class
|
|
85
|
+
ON pg_index.indrelid = table_class.oid
|
|
86
|
+
JOIN pg_class AS idx_class
|
|
87
|
+
ON pg_index.indexrelid = idx_class.oid
|
|
88
|
+
JOIN pg_namespace
|
|
89
|
+
ON table_class.relnamespace = pg_namespace.oid
|
|
90
|
+
JOIN pg_attribute
|
|
91
|
+
ON pg_attribute.attrelid = table_class.oid AND pg_attribute.attnum = ANY (pg_index.indkey)
|
|
92
|
+
WHERE pg_namespace.nspname = ?
|
|
93
|
+
AND indisprimary = FALSE
|
|
94
|
+
AND indisunique = FALSE
|
|
95
|
+
GROUP BY pg_index.indexrelid
|
|
96
|
+
`, [schema]))
|
|
97
|
+
.rows
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
enum ConstraintTypes {
|
|
102
|
+
foreignKey = 'f',
|
|
103
|
+
unique = 'u',
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type ConstraintsRow =
|
|
107
|
+
| ForeignKeyConstraintsRow
|
|
108
|
+
| UniqueConstraintsRow
|
|
109
|
+
|
|
110
|
+
type ForeignKeyConstraintsRow = {
|
|
111
|
+
constraint_name: string
|
|
112
|
+
table_name: string
|
|
113
|
+
columns: string[]
|
|
114
|
+
target_table: string
|
|
115
|
+
target_columns: string[]
|
|
116
|
+
type: ConstraintTypes.foreignKey
|
|
117
|
+
deferrable: boolean
|
|
118
|
+
deferred: boolean
|
|
119
|
+
}
|
|
120
|
+
type UniqueConstraintsRow = {
|
|
121
|
+
constraint_name: string
|
|
122
|
+
table_name: string
|
|
123
|
+
columns: string[]
|
|
124
|
+
target_table: null
|
|
125
|
+
target_columns: null
|
|
126
|
+
type: ConstraintTypes.unique
|
|
127
|
+
deferrable: boolean
|
|
128
|
+
deferred: boolean
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type IndexRow = {
|
|
132
|
+
index_name: string
|
|
133
|
+
table_name: string
|
|
134
|
+
columns: string[]
|
|
135
|
+
}
|
|
136
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
2
|
+
import { SchemaDatabaseMetadataResolver } from './SchemaDatabaseMetadataResolver'
|
|
3
|
+
import { DatabaseContext } from '../database'
|
|
4
|
+
|
|
5
|
+
export class SchemaDatabaseMetadataResolverStore {
|
|
6
|
+
|
|
7
|
+
private cache: Map<string, SchemaDatabaseMetadata> = new Map()
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
private resolver: SchemaDatabaseMetadataResolver,
|
|
11
|
+
private db: DatabaseContext,
|
|
12
|
+
) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async getMetadata(schema: string): Promise<SchemaDatabaseMetadata> {
|
|
16
|
+
const cached = this.cache.get(schema)
|
|
17
|
+
if (cached) {
|
|
18
|
+
return cached
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const newValue = await this.resolver.resolveMetadata(this.db, schema)
|
|
22
|
+
this.cache.set(schema, newValue)
|
|
23
|
+
return newValue
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public invalidate(schema: string): void {
|
|
27
|
+
this.cache.delete(schema)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
export class MigrationsDatabaseMetadataResolverStoreFactory {
|
|
33
|
+
constructor(
|
|
34
|
+
private resolver: SchemaDatabaseMetadataResolver,
|
|
35
|
+
) {
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public create(db: DatabaseContext) {
|
|
39
|
+
return new SchemaDatabaseMetadataResolverStore(this.resolver, db)
|
|
40
|
+
}
|
|
41
|
+
}
|