@pgpmjs/core 4.7.1 → 4.8.0
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/esm/export/export-meta.js +32 -32
- package/esm/export/export-migrations.js +26 -12
- package/esm/modules/modules.js +2 -2
- package/export/export-meta.js +32 -32
- package/export/export-migrations.d.ts +7 -1
- package/export/export-migrations.js +26 -12
- package/modules/modules.js +2 -2
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import { Parser } from 'csv-to-pg';
|
|
|
2
2
|
import { getPgPool } from 'pg-cache';
|
|
3
3
|
const config = {
|
|
4
4
|
database: {
|
|
5
|
-
schema: '
|
|
5
|
+
schema: 'metaschema_public',
|
|
6
6
|
table: 'database',
|
|
7
7
|
fields: {
|
|
8
8
|
id: 'uuid',
|
|
@@ -12,7 +12,7 @@ const config = {
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
database_extension: {
|
|
15
|
-
schema: '
|
|
15
|
+
schema: 'metaschema_public',
|
|
16
16
|
table: 'database_extension',
|
|
17
17
|
fields: {
|
|
18
18
|
name: 'text',
|
|
@@ -20,7 +20,7 @@ const config = {
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
schema: {
|
|
23
|
-
schema: '
|
|
23
|
+
schema: 'metaschema_public',
|
|
24
24
|
table: 'schema',
|
|
25
25
|
fields: {
|
|
26
26
|
id: 'uuid',
|
|
@@ -31,7 +31,7 @@ const config = {
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
table: {
|
|
34
|
-
schema: '
|
|
34
|
+
schema: 'metaschema_public',
|
|
35
35
|
table: 'table',
|
|
36
36
|
fields: {
|
|
37
37
|
id: 'uuid',
|
|
@@ -42,7 +42,7 @@ const config = {
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
field: {
|
|
45
|
-
schema: '
|
|
45
|
+
schema: 'metaschema_public',
|
|
46
46
|
table: 'field',
|
|
47
47
|
fields: {
|
|
48
48
|
id: 'uuid',
|
|
@@ -54,7 +54,7 @@ const config = {
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
domains: {
|
|
57
|
-
schema: '
|
|
57
|
+
schema: 'services_public',
|
|
58
58
|
table: 'domains',
|
|
59
59
|
fields: {
|
|
60
60
|
id: 'uuid',
|
|
@@ -66,7 +66,7 @@ const config = {
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
sites: {
|
|
69
|
-
schema: '
|
|
69
|
+
schema: 'services_public',
|
|
70
70
|
table: 'sites',
|
|
71
71
|
fields: {
|
|
72
72
|
id: 'uuid',
|
|
@@ -81,7 +81,7 @@ const config = {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
apis: {
|
|
84
|
-
schema: '
|
|
84
|
+
schema: 'services_public',
|
|
85
85
|
table: 'apis',
|
|
86
86
|
fields: {
|
|
87
87
|
id: 'uuid',
|
|
@@ -94,7 +94,7 @@ const config = {
|
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
96
|
apps: {
|
|
97
|
-
schema: '
|
|
97
|
+
schema: 'services_public',
|
|
98
98
|
table: 'apps',
|
|
99
99
|
fields: {
|
|
100
100
|
id: 'uuid',
|
|
@@ -109,7 +109,7 @@ const config = {
|
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
111
|
site_modules: {
|
|
112
|
-
schema: '
|
|
112
|
+
schema: 'services_public',
|
|
113
113
|
table: 'site_modules',
|
|
114
114
|
fields: {
|
|
115
115
|
id: 'uuid',
|
|
@@ -120,7 +120,7 @@ const config = {
|
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
122
|
site_themes: {
|
|
123
|
-
schema: '
|
|
123
|
+
schema: 'services_public',
|
|
124
124
|
table: 'site_themes',
|
|
125
125
|
fields: {
|
|
126
126
|
id: 'uuid',
|
|
@@ -130,7 +130,7 @@ const config = {
|
|
|
130
130
|
}
|
|
131
131
|
},
|
|
132
132
|
api_modules: {
|
|
133
|
-
schema: '
|
|
133
|
+
schema: 'services_public',
|
|
134
134
|
table: 'api_modules',
|
|
135
135
|
fields: {
|
|
136
136
|
id: 'uuid',
|
|
@@ -141,7 +141,7 @@ const config = {
|
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
143
|
api_extensions: {
|
|
144
|
-
schema: '
|
|
144
|
+
schema: 'services_public',
|
|
145
145
|
table: 'api_extensions',
|
|
146
146
|
fields: {
|
|
147
147
|
id: 'uuid',
|
|
@@ -151,7 +151,7 @@ const config = {
|
|
|
151
151
|
}
|
|
152
152
|
},
|
|
153
153
|
api_schemata: {
|
|
154
|
-
schema: '
|
|
154
|
+
schema: 'services_public',
|
|
155
155
|
table: 'api_schemata',
|
|
156
156
|
fields: {
|
|
157
157
|
id: 'uuid',
|
|
@@ -161,7 +161,7 @@ const config = {
|
|
|
161
161
|
}
|
|
162
162
|
},
|
|
163
163
|
rls_module: {
|
|
164
|
-
schema: '
|
|
164
|
+
schema: 'metaschema_modules_public',
|
|
165
165
|
table: 'rls_module',
|
|
166
166
|
fields: {
|
|
167
167
|
id: 'uuid',
|
|
@@ -178,7 +178,7 @@ const config = {
|
|
|
178
178
|
}
|
|
179
179
|
},
|
|
180
180
|
user_auth_module: {
|
|
181
|
-
schema: '
|
|
181
|
+
schema: 'metaschema_modules_public',
|
|
182
182
|
table: 'user_auth_module',
|
|
183
183
|
fields: {
|
|
184
184
|
id: 'uuid',
|
|
@@ -224,21 +224,21 @@ export const exportMeta = async ({ opts, dbname, database_id }) => {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
};
|
|
227
|
-
await queryAndParse('database', `SELECT * FROM
|
|
228
|
-
await queryAndParse('schema', `SELECT * FROM
|
|
229
|
-
await queryAndParse('table', `SELECT * FROM
|
|
230
|
-
await queryAndParse('field', `SELECT * FROM
|
|
231
|
-
await queryAndParse('domains', `SELECT * FROM
|
|
232
|
-
await queryAndParse('apis', `SELECT * FROM
|
|
233
|
-
await queryAndParse('sites', `SELECT * FROM
|
|
234
|
-
await queryAndParse('api_modules', `SELECT * FROM
|
|
235
|
-
await queryAndParse('site_modules', `SELECT * FROM
|
|
236
|
-
await queryAndParse('site_themes', `SELECT * FROM
|
|
237
|
-
await queryAndParse('apps', `SELECT * FROM
|
|
238
|
-
await queryAndParse('database_extension', `SELECT * FROM
|
|
239
|
-
await queryAndParse('api_extensions', `SELECT * FROM
|
|
240
|
-
await queryAndParse('api_schemata', `SELECT * FROM
|
|
241
|
-
await queryAndParse('rls_module', `SELECT * FROM
|
|
242
|
-
await queryAndParse('user_auth_module', `SELECT * FROM
|
|
227
|
+
await queryAndParse('database', `SELECT * FROM metaschema_public.database WHERE id = $1`);
|
|
228
|
+
await queryAndParse('schema', `SELECT * FROM metaschema_public.schema WHERE database_id = $1`);
|
|
229
|
+
await queryAndParse('table', `SELECT * FROM metaschema_public.table WHERE database_id = $1`);
|
|
230
|
+
await queryAndParse('field', `SELECT * FROM metaschema_public.field WHERE database_id = $1`);
|
|
231
|
+
await queryAndParse('domains', `SELECT * FROM services_public.domains WHERE database_id = $1`);
|
|
232
|
+
await queryAndParse('apis', `SELECT * FROM services_public.apis WHERE database_id = $1`);
|
|
233
|
+
await queryAndParse('sites', `SELECT * FROM services_public.sites WHERE database_id = $1`);
|
|
234
|
+
await queryAndParse('api_modules', `SELECT * FROM services_public.api_modules WHERE database_id = $1`);
|
|
235
|
+
await queryAndParse('site_modules', `SELECT * FROM services_public.site_modules WHERE database_id = $1`);
|
|
236
|
+
await queryAndParse('site_themes', `SELECT * FROM services_public.site_themes WHERE database_id = $1`);
|
|
237
|
+
await queryAndParse('apps', `SELECT * FROM services_public.apps WHERE database_id = $1`);
|
|
238
|
+
await queryAndParse('database_extension', `SELECT * FROM metaschema_public.database_extension WHERE database_id = $1`);
|
|
239
|
+
await queryAndParse('api_extensions', `SELECT * FROM services_public.api_extensions WHERE database_id = $1`);
|
|
240
|
+
await queryAndParse('api_schemata', `SELECT * FROM services_public.api_schemata WHERE database_id = $1`);
|
|
241
|
+
await queryAndParse('rls_module', `SELECT * FROM metaschema_modules_public.rls_module WHERE database_id = $1`);
|
|
242
|
+
await queryAndParse('user_auth_module', `SELECT * FROM metaschema_modules_public.user_auth_module WHERE database_id = $1`);
|
|
243
243
|
return Object.entries(sql).reduce((m, [_, v]) => m + '\n\n' + v, '');
|
|
244
244
|
};
|
|
@@ -92,7 +92,7 @@ const installMissingModules = async (moduleDir, missingModules) => {
|
|
|
92
92
|
await moduleProject.installModules(...missingNames);
|
|
93
93
|
console.log('Modules installed successfully.');
|
|
94
94
|
};
|
|
95
|
-
const exportMigrationsToDisk = async ({ project, options, database, databaseId, databaseName, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir }) => {
|
|
95
|
+
const exportMigrationsToDisk = async ({ project, options, database, databaseId, databaseName, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir, skipSchemaRenaming = false }) => {
|
|
96
96
|
outdir = outdir + '/';
|
|
97
97
|
// Use serviceOutdir for service module, defaulting to outdir if not provided
|
|
98
98
|
const svcOutdir = (serviceOutdir || outdir.slice(0, -1)) + '/';
|
|
@@ -100,8 +100,8 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
100
100
|
...options.pg,
|
|
101
101
|
database
|
|
102
102
|
});
|
|
103
|
-
const db = await pgPool.query(`select * from
|
|
104
|
-
const schemas = await pgPool.query(`select * from
|
|
103
|
+
const db = await pgPool.query(`select * from metaschema_public.database where id=$1`, [databaseId]);
|
|
104
|
+
const schemas = await pgPool.query(`select * from metaschema_public.schema where database_id=$1`, [databaseId]);
|
|
105
105
|
if (!db?.rows?.length) {
|
|
106
106
|
console.log('NO DATABASES.');
|
|
107
107
|
return;
|
|
@@ -111,11 +111,20 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
const name = extensionName || db.rows[0].name;
|
|
114
|
+
// When skipSchemaRenaming is true, pass empty schemas array to avoid renaming
|
|
115
|
+
// This is useful for self-referential introspection where you want to apply
|
|
116
|
+
// policies to real infrastructure schemas (metaschema_public, services_public, etc.)
|
|
117
|
+
const schemasForReplacement = skipSchemaRenaming
|
|
118
|
+
? []
|
|
119
|
+
: schemas.rows.filter((schema) => schema_names.includes(schema.schema_name));
|
|
114
120
|
const { replace, replacer } = makeReplacer({
|
|
115
|
-
schemas:
|
|
121
|
+
schemas: schemasForReplacement,
|
|
116
122
|
name
|
|
117
123
|
});
|
|
118
|
-
|
|
124
|
+
// Filter sql_actions by database_id to avoid cross-database pollution
|
|
125
|
+
// Previously this query had no WHERE clause, which could export actions
|
|
126
|
+
// from unrelated databases in a persistent database environment
|
|
127
|
+
const results = await pgPool.query(`select * from db_migrate.sql_actions where database_id = $1 order by id`, [databaseId]);
|
|
119
128
|
const opts = {
|
|
120
129
|
name,
|
|
121
130
|
replacer,
|
|
@@ -171,8 +180,12 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
171
180
|
if (svcMissingResult.shouldInstall) {
|
|
172
181
|
await installMissingModules(svcModuleDir, svcMissingResult.missingModules);
|
|
173
182
|
}
|
|
183
|
+
// Use same skipSchemaRenaming logic for meta replacer
|
|
184
|
+
const metaSchemasForReplacement = skipSchemaRenaming
|
|
185
|
+
? []
|
|
186
|
+
: schemas.rows.filter((schema) => schema_names.includes(schema.schema_name));
|
|
174
187
|
const metaReplacer = makeReplacer({
|
|
175
|
-
schemas:
|
|
188
|
+
schemas: metaSchemasForReplacement,
|
|
176
189
|
name: metaExtensionName
|
|
177
190
|
});
|
|
178
191
|
const metaPackage = [
|
|
@@ -180,7 +193,7 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
180
193
|
deps: [],
|
|
181
194
|
deploy: 'migrate/meta',
|
|
182
195
|
content: `SET session_replication_role TO replica;
|
|
183
|
-
-- using replica in case we are deploying triggers to
|
|
196
|
+
-- using replica in case we are deploying triggers to metaschema_public
|
|
184
197
|
|
|
185
198
|
-- unaccent, postgis affected and require grants
|
|
186
199
|
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public to public;
|
|
@@ -199,12 +212,12 @@ ${meta}
|
|
|
199
212
|
|
|
200
213
|
-- TODO: Research needed - These UPDATE statements may be a security leak.
|
|
201
214
|
-- They appear to rebind exported metadata to the target database after import,
|
|
202
|
-
-- but exposing dbname in
|
|
215
|
+
-- but exposing dbname in services_public tables could leak internal database names.
|
|
203
216
|
-- Consider removing entirely or gating behind an explicit flag.
|
|
204
|
-
-- UPDATE
|
|
217
|
+
-- UPDATE services_public.apis
|
|
205
218
|
-- SET dbname = current_database() WHERE TRUE;
|
|
206
219
|
|
|
207
|
-
-- UPDATE
|
|
220
|
+
-- UPDATE services_public.sites
|
|
208
221
|
-- SET dbname = current_database() WHERE TRUE;
|
|
209
222
|
|
|
210
223
|
SET session_replication_role TO DEFAULT;
|
|
@@ -219,7 +232,7 @@ SET session_replication_role TO DEFAULT;
|
|
|
219
232
|
}
|
|
220
233
|
pgPool.end();
|
|
221
234
|
};
|
|
222
|
-
export const exportMigrations = async ({ project, options, dbInfo, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir }) => {
|
|
235
|
+
export const exportMigrations = async ({ project, options, dbInfo, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir, skipSchemaRenaming }) => {
|
|
223
236
|
for (let v = 0; v < dbInfo.database_ids.length; v++) {
|
|
224
237
|
const databaseId = dbInfo.database_ids[v];
|
|
225
238
|
await exportMigrationsToDisk({
|
|
@@ -238,7 +251,8 @@ export const exportMigrations = async ({ project, options, dbInfo, author, outdi
|
|
|
238
251
|
prompter,
|
|
239
252
|
repoName,
|
|
240
253
|
username,
|
|
241
|
-
serviceOutdir
|
|
254
|
+
serviceOutdir,
|
|
255
|
+
skipSchemaRenaming
|
|
242
256
|
});
|
|
243
257
|
}
|
|
244
258
|
};
|
package/esm/modules/modules.js
CHANGED
|
@@ -8,8 +8,8 @@ import { errors } from '@pgpmjs/types';
|
|
|
8
8
|
export const PGPM_MODULE_MAP = {
|
|
9
9
|
'pgpm-base32': '@pgpm/base32',
|
|
10
10
|
'pgpm-database-jobs': '@pgpm/database-jobs',
|
|
11
|
-
'
|
|
12
|
-
'
|
|
11
|
+
'metaschema-modules': '@pgpm/metaschema-modules',
|
|
12
|
+
'metaschema-schema': '@pgpm/metaschema-schema',
|
|
13
13
|
'pgpm-inflection': '@pgpm/inflection',
|
|
14
14
|
'pgpm-jwt-claims': '@pgpm/jwt-claims',
|
|
15
15
|
'pgpm-stamps': '@pgpm/stamps',
|
package/export/export-meta.js
CHANGED
|
@@ -5,7 +5,7 @@ const csv_to_pg_1 = require("csv-to-pg");
|
|
|
5
5
|
const pg_cache_1 = require("pg-cache");
|
|
6
6
|
const config = {
|
|
7
7
|
database: {
|
|
8
|
-
schema: '
|
|
8
|
+
schema: 'metaschema_public',
|
|
9
9
|
table: 'database',
|
|
10
10
|
fields: {
|
|
11
11
|
id: 'uuid',
|
|
@@ -15,7 +15,7 @@ const config = {
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
database_extension: {
|
|
18
|
-
schema: '
|
|
18
|
+
schema: 'metaschema_public',
|
|
19
19
|
table: 'database_extension',
|
|
20
20
|
fields: {
|
|
21
21
|
name: 'text',
|
|
@@ -23,7 +23,7 @@ const config = {
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
schema: {
|
|
26
|
-
schema: '
|
|
26
|
+
schema: 'metaschema_public',
|
|
27
27
|
table: 'schema',
|
|
28
28
|
fields: {
|
|
29
29
|
id: 'uuid',
|
|
@@ -34,7 +34,7 @@ const config = {
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
table: {
|
|
37
|
-
schema: '
|
|
37
|
+
schema: 'metaschema_public',
|
|
38
38
|
table: 'table',
|
|
39
39
|
fields: {
|
|
40
40
|
id: 'uuid',
|
|
@@ -45,7 +45,7 @@ const config = {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
field: {
|
|
48
|
-
schema: '
|
|
48
|
+
schema: 'metaschema_public',
|
|
49
49
|
table: 'field',
|
|
50
50
|
fields: {
|
|
51
51
|
id: 'uuid',
|
|
@@ -57,7 +57,7 @@ const config = {
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
domains: {
|
|
60
|
-
schema: '
|
|
60
|
+
schema: 'services_public',
|
|
61
61
|
table: 'domains',
|
|
62
62
|
fields: {
|
|
63
63
|
id: 'uuid',
|
|
@@ -69,7 +69,7 @@ const config = {
|
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
sites: {
|
|
72
|
-
schema: '
|
|
72
|
+
schema: 'services_public',
|
|
73
73
|
table: 'sites',
|
|
74
74
|
fields: {
|
|
75
75
|
id: 'uuid',
|
|
@@ -84,7 +84,7 @@ const config = {
|
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
86
|
apis: {
|
|
87
|
-
schema: '
|
|
87
|
+
schema: 'services_public',
|
|
88
88
|
table: 'apis',
|
|
89
89
|
fields: {
|
|
90
90
|
id: 'uuid',
|
|
@@ -97,7 +97,7 @@ const config = {
|
|
|
97
97
|
}
|
|
98
98
|
},
|
|
99
99
|
apps: {
|
|
100
|
-
schema: '
|
|
100
|
+
schema: 'services_public',
|
|
101
101
|
table: 'apps',
|
|
102
102
|
fields: {
|
|
103
103
|
id: 'uuid',
|
|
@@ -112,7 +112,7 @@ const config = {
|
|
|
112
112
|
}
|
|
113
113
|
},
|
|
114
114
|
site_modules: {
|
|
115
|
-
schema: '
|
|
115
|
+
schema: 'services_public',
|
|
116
116
|
table: 'site_modules',
|
|
117
117
|
fields: {
|
|
118
118
|
id: 'uuid',
|
|
@@ -123,7 +123,7 @@ const config = {
|
|
|
123
123
|
}
|
|
124
124
|
},
|
|
125
125
|
site_themes: {
|
|
126
|
-
schema: '
|
|
126
|
+
schema: 'services_public',
|
|
127
127
|
table: 'site_themes',
|
|
128
128
|
fields: {
|
|
129
129
|
id: 'uuid',
|
|
@@ -133,7 +133,7 @@ const config = {
|
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
135
|
api_modules: {
|
|
136
|
-
schema: '
|
|
136
|
+
schema: 'services_public',
|
|
137
137
|
table: 'api_modules',
|
|
138
138
|
fields: {
|
|
139
139
|
id: 'uuid',
|
|
@@ -144,7 +144,7 @@ const config = {
|
|
|
144
144
|
}
|
|
145
145
|
},
|
|
146
146
|
api_extensions: {
|
|
147
|
-
schema: '
|
|
147
|
+
schema: 'services_public',
|
|
148
148
|
table: 'api_extensions',
|
|
149
149
|
fields: {
|
|
150
150
|
id: 'uuid',
|
|
@@ -154,7 +154,7 @@ const config = {
|
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
api_schemata: {
|
|
157
|
-
schema: '
|
|
157
|
+
schema: 'services_public',
|
|
158
158
|
table: 'api_schemata',
|
|
159
159
|
fields: {
|
|
160
160
|
id: 'uuid',
|
|
@@ -164,7 +164,7 @@ const config = {
|
|
|
164
164
|
}
|
|
165
165
|
},
|
|
166
166
|
rls_module: {
|
|
167
|
-
schema: '
|
|
167
|
+
schema: 'metaschema_modules_public',
|
|
168
168
|
table: 'rls_module',
|
|
169
169
|
fields: {
|
|
170
170
|
id: 'uuid',
|
|
@@ -181,7 +181,7 @@ const config = {
|
|
|
181
181
|
}
|
|
182
182
|
},
|
|
183
183
|
user_auth_module: {
|
|
184
|
-
schema: '
|
|
184
|
+
schema: 'metaschema_modules_public',
|
|
185
185
|
table: 'user_auth_module',
|
|
186
186
|
fields: {
|
|
187
187
|
id: 'uuid',
|
|
@@ -227,22 +227,22 @@ const exportMeta = async ({ opts, dbname, database_id }) => {
|
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
};
|
|
230
|
-
await queryAndParse('database', `SELECT * FROM
|
|
231
|
-
await queryAndParse('schema', `SELECT * FROM
|
|
232
|
-
await queryAndParse('table', `SELECT * FROM
|
|
233
|
-
await queryAndParse('field', `SELECT * FROM
|
|
234
|
-
await queryAndParse('domains', `SELECT * FROM
|
|
235
|
-
await queryAndParse('apis', `SELECT * FROM
|
|
236
|
-
await queryAndParse('sites', `SELECT * FROM
|
|
237
|
-
await queryAndParse('api_modules', `SELECT * FROM
|
|
238
|
-
await queryAndParse('site_modules', `SELECT * FROM
|
|
239
|
-
await queryAndParse('site_themes', `SELECT * FROM
|
|
240
|
-
await queryAndParse('apps', `SELECT * FROM
|
|
241
|
-
await queryAndParse('database_extension', `SELECT * FROM
|
|
242
|
-
await queryAndParse('api_extensions', `SELECT * FROM
|
|
243
|
-
await queryAndParse('api_schemata', `SELECT * FROM
|
|
244
|
-
await queryAndParse('rls_module', `SELECT * FROM
|
|
245
|
-
await queryAndParse('user_auth_module', `SELECT * FROM
|
|
230
|
+
await queryAndParse('database', `SELECT * FROM metaschema_public.database WHERE id = $1`);
|
|
231
|
+
await queryAndParse('schema', `SELECT * FROM metaschema_public.schema WHERE database_id = $1`);
|
|
232
|
+
await queryAndParse('table', `SELECT * FROM metaschema_public.table WHERE database_id = $1`);
|
|
233
|
+
await queryAndParse('field', `SELECT * FROM metaschema_public.field WHERE database_id = $1`);
|
|
234
|
+
await queryAndParse('domains', `SELECT * FROM services_public.domains WHERE database_id = $1`);
|
|
235
|
+
await queryAndParse('apis', `SELECT * FROM services_public.apis WHERE database_id = $1`);
|
|
236
|
+
await queryAndParse('sites', `SELECT * FROM services_public.sites WHERE database_id = $1`);
|
|
237
|
+
await queryAndParse('api_modules', `SELECT * FROM services_public.api_modules WHERE database_id = $1`);
|
|
238
|
+
await queryAndParse('site_modules', `SELECT * FROM services_public.site_modules WHERE database_id = $1`);
|
|
239
|
+
await queryAndParse('site_themes', `SELECT * FROM services_public.site_themes WHERE database_id = $1`);
|
|
240
|
+
await queryAndParse('apps', `SELECT * FROM services_public.apps WHERE database_id = $1`);
|
|
241
|
+
await queryAndParse('database_extension', `SELECT * FROM metaschema_public.database_extension WHERE database_id = $1`);
|
|
242
|
+
await queryAndParse('api_extensions', `SELECT * FROM services_public.api_extensions WHERE database_id = $1`);
|
|
243
|
+
await queryAndParse('api_schemata', `SELECT * FROM services_public.api_schemata WHERE database_id = $1`);
|
|
244
|
+
await queryAndParse('rls_module', `SELECT * FROM metaschema_modules_public.rls_module WHERE database_id = $1`);
|
|
245
|
+
await queryAndParse('user_auth_module', `SELECT * FROM metaschema_modules_public.user_auth_module WHERE database_id = $1`);
|
|
246
246
|
return Object.entries(sql).reduce((m, [_, v]) => m + '\n\n' + v, '');
|
|
247
247
|
};
|
|
248
248
|
exports.exportMeta = exportMeta;
|
|
@@ -29,6 +29,12 @@ interface ExportOptions {
|
|
|
29
29
|
username?: string;
|
|
30
30
|
/** Output directory for service/meta module. Defaults to outdir if not provided. */
|
|
31
31
|
serviceOutdir?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Skip schema name replacement for infrastructure schemas.
|
|
34
|
+
* When true, schema names like metaschema_public, services_public will not be renamed.
|
|
35
|
+
* Useful for self-referential introspection where you want to apply policies to real schemas.
|
|
36
|
+
*/
|
|
37
|
+
skipSchemaRenaming?: boolean;
|
|
32
38
|
}
|
|
33
|
-
export declare const exportMigrations: ({ project, options, dbInfo, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir }: ExportOptions) => Promise<void>;
|
|
39
|
+
export declare const exportMigrations: ({ project, options, dbInfo, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir, skipSchemaRenaming }: ExportOptions) => Promise<void>;
|
|
34
40
|
export {};
|
|
@@ -98,7 +98,7 @@ const installMissingModules = async (moduleDir, missingModules) => {
|
|
|
98
98
|
await moduleProject.installModules(...missingNames);
|
|
99
99
|
console.log('Modules installed successfully.');
|
|
100
100
|
};
|
|
101
|
-
const exportMigrationsToDisk = async ({ project, options, database, databaseId, databaseName, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir }) => {
|
|
101
|
+
const exportMigrationsToDisk = async ({ project, options, database, databaseId, databaseName, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir, skipSchemaRenaming = false }) => {
|
|
102
102
|
outdir = outdir + '/';
|
|
103
103
|
// Use serviceOutdir for service module, defaulting to outdir if not provided
|
|
104
104
|
const svcOutdir = (serviceOutdir || outdir.slice(0, -1)) + '/';
|
|
@@ -106,8 +106,8 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
106
106
|
...options.pg,
|
|
107
107
|
database
|
|
108
108
|
});
|
|
109
|
-
const db = await pgPool.query(`select * from
|
|
110
|
-
const schemas = await pgPool.query(`select * from
|
|
109
|
+
const db = await pgPool.query(`select * from metaschema_public.database where id=$1`, [databaseId]);
|
|
110
|
+
const schemas = await pgPool.query(`select * from metaschema_public.schema where database_id=$1`, [databaseId]);
|
|
111
111
|
if (!db?.rows?.length) {
|
|
112
112
|
console.log('NO DATABASES.');
|
|
113
113
|
return;
|
|
@@ -117,11 +117,20 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
119
|
const name = extensionName || db.rows[0].name;
|
|
120
|
+
// When skipSchemaRenaming is true, pass empty schemas array to avoid renaming
|
|
121
|
+
// This is useful for self-referential introspection where you want to apply
|
|
122
|
+
// policies to real infrastructure schemas (metaschema_public, services_public, etc.)
|
|
123
|
+
const schemasForReplacement = skipSchemaRenaming
|
|
124
|
+
? []
|
|
125
|
+
: schemas.rows.filter((schema) => schema_names.includes(schema.schema_name));
|
|
120
126
|
const { replace, replacer } = makeReplacer({
|
|
121
|
-
schemas:
|
|
127
|
+
schemas: schemasForReplacement,
|
|
122
128
|
name
|
|
123
129
|
});
|
|
124
|
-
|
|
130
|
+
// Filter sql_actions by database_id to avoid cross-database pollution
|
|
131
|
+
// Previously this query had no WHERE clause, which could export actions
|
|
132
|
+
// from unrelated databases in a persistent database environment
|
|
133
|
+
const results = await pgPool.query(`select * from db_migrate.sql_actions where database_id = $1 order by id`, [databaseId]);
|
|
125
134
|
const opts = {
|
|
126
135
|
name,
|
|
127
136
|
replacer,
|
|
@@ -177,8 +186,12 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
177
186
|
if (svcMissingResult.shouldInstall) {
|
|
178
187
|
await installMissingModules(svcModuleDir, svcMissingResult.missingModules);
|
|
179
188
|
}
|
|
189
|
+
// Use same skipSchemaRenaming logic for meta replacer
|
|
190
|
+
const metaSchemasForReplacement = skipSchemaRenaming
|
|
191
|
+
? []
|
|
192
|
+
: schemas.rows.filter((schema) => schema_names.includes(schema.schema_name));
|
|
180
193
|
const metaReplacer = makeReplacer({
|
|
181
|
-
schemas:
|
|
194
|
+
schemas: metaSchemasForReplacement,
|
|
182
195
|
name: metaExtensionName
|
|
183
196
|
});
|
|
184
197
|
const metaPackage = [
|
|
@@ -186,7 +199,7 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
186
199
|
deps: [],
|
|
187
200
|
deploy: 'migrate/meta',
|
|
188
201
|
content: `SET session_replication_role TO replica;
|
|
189
|
-
-- using replica in case we are deploying triggers to
|
|
202
|
+
-- using replica in case we are deploying triggers to metaschema_public
|
|
190
203
|
|
|
191
204
|
-- unaccent, postgis affected and require grants
|
|
192
205
|
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public to public;
|
|
@@ -205,12 +218,12 @@ ${meta}
|
|
|
205
218
|
|
|
206
219
|
-- TODO: Research needed - These UPDATE statements may be a security leak.
|
|
207
220
|
-- They appear to rebind exported metadata to the target database after import,
|
|
208
|
-
-- but exposing dbname in
|
|
221
|
+
-- but exposing dbname in services_public tables could leak internal database names.
|
|
209
222
|
-- Consider removing entirely or gating behind an explicit flag.
|
|
210
|
-
-- UPDATE
|
|
223
|
+
-- UPDATE services_public.apis
|
|
211
224
|
-- SET dbname = current_database() WHERE TRUE;
|
|
212
225
|
|
|
213
|
-
-- UPDATE
|
|
226
|
+
-- UPDATE services_public.sites
|
|
214
227
|
-- SET dbname = current_database() WHERE TRUE;
|
|
215
228
|
|
|
216
229
|
SET session_replication_role TO DEFAULT;
|
|
@@ -225,7 +238,7 @@ SET session_replication_role TO DEFAULT;
|
|
|
225
238
|
}
|
|
226
239
|
pgPool.end();
|
|
227
240
|
};
|
|
228
|
-
const exportMigrations = async ({ project, options, dbInfo, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir }) => {
|
|
241
|
+
const exportMigrations = async ({ project, options, dbInfo, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter, repoName, username, serviceOutdir, skipSchemaRenaming }) => {
|
|
229
242
|
for (let v = 0; v < dbInfo.database_ids.length; v++) {
|
|
230
243
|
const databaseId = dbInfo.database_ids[v];
|
|
231
244
|
await exportMigrationsToDisk({
|
|
@@ -244,7 +257,8 @@ const exportMigrations = async ({ project, options, dbInfo, author, outdir, sche
|
|
|
244
257
|
prompter,
|
|
245
258
|
repoName,
|
|
246
259
|
username,
|
|
247
|
-
serviceOutdir
|
|
260
|
+
serviceOutdir,
|
|
261
|
+
skipSchemaRenaming
|
|
248
262
|
});
|
|
249
263
|
}
|
|
250
264
|
};
|
package/modules/modules.js
CHANGED
|
@@ -11,8 +11,8 @@ const types_1 = require("@pgpmjs/types");
|
|
|
11
11
|
exports.PGPM_MODULE_MAP = {
|
|
12
12
|
'pgpm-base32': '@pgpm/base32',
|
|
13
13
|
'pgpm-database-jobs': '@pgpm/database-jobs',
|
|
14
|
-
'
|
|
15
|
-
'
|
|
14
|
+
'metaschema-modules': '@pgpm/metaschema-modules',
|
|
15
|
+
'metaschema-schema': '@pgpm/metaschema-schema',
|
|
16
16
|
'pgpm-inflection': '@pgpm/inflection',
|
|
17
17
|
'pgpm-jwt-claims': '@pgpm/jwt-claims',
|
|
18
18
|
'pgpm-stamps': '@pgpm/stamps',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpmjs/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PGPM Package and Migration Tools",
|
|
6
6
|
"main": "index.js",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"pgsql-parser": "^17.9.9",
|
|
65
65
|
"yanse": "^0.1.11"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "97528ad4eb2f60c16785ffb84af7b61c52cb5ad8"
|
|
68
68
|
}
|