@pgpmjs/core 3.0.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.
Files changed (140) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +99 -0
  3. package/core/boilerplate-scanner.d.ts +41 -0
  4. package/core/boilerplate-scanner.js +106 -0
  5. package/core/boilerplate-types.d.ts +52 -0
  6. package/core/boilerplate-types.js +6 -0
  7. package/core/class/pgpm.d.ts +150 -0
  8. package/core/class/pgpm.js +1470 -0
  9. package/core/template-scaffold.d.ts +29 -0
  10. package/core/template-scaffold.js +168 -0
  11. package/esm/core/boilerplate-scanner.js +96 -0
  12. package/esm/core/boilerplate-types.js +5 -0
  13. package/esm/core/class/pgpm.js +1430 -0
  14. package/esm/core/template-scaffold.js +161 -0
  15. package/esm/export/export-meta.js +240 -0
  16. package/esm/export/export-migrations.js +180 -0
  17. package/esm/extensions/extensions.js +31 -0
  18. package/esm/files/extension/index.js +3 -0
  19. package/esm/files/extension/reader.js +79 -0
  20. package/esm/files/extension/writer.js +63 -0
  21. package/esm/files/index.js +6 -0
  22. package/esm/files/plan/generator.js +49 -0
  23. package/esm/files/plan/index.js +5 -0
  24. package/esm/files/plan/parser.js +296 -0
  25. package/esm/files/plan/validators.js +181 -0
  26. package/esm/files/plan/writer.js +114 -0
  27. package/esm/files/sql/index.js +1 -0
  28. package/esm/files/sql/writer.js +107 -0
  29. package/esm/files/sql-scripts/index.js +2 -0
  30. package/esm/files/sql-scripts/reader.js +19 -0
  31. package/esm/files/types/index.js +1 -0
  32. package/esm/files/types/package.js +1 -0
  33. package/esm/index.js +21 -0
  34. package/esm/init/client.js +144 -0
  35. package/esm/init/sql/bootstrap-roles.sql +55 -0
  36. package/esm/init/sql/bootstrap-test-roles.sql +72 -0
  37. package/esm/migrate/clean.js +23 -0
  38. package/esm/migrate/client.js +551 -0
  39. package/esm/migrate/index.js +5 -0
  40. package/esm/migrate/sql/procedures.sql +258 -0
  41. package/esm/migrate/sql/schema.sql +37 -0
  42. package/esm/migrate/types.js +1 -0
  43. package/esm/migrate/utils/event-logger.js +28 -0
  44. package/esm/migrate/utils/hash.js +27 -0
  45. package/esm/migrate/utils/transaction.js +125 -0
  46. package/esm/modules/modules.js +49 -0
  47. package/esm/packaging/package.js +96 -0
  48. package/esm/packaging/transform.js +70 -0
  49. package/esm/projects/deploy.js +123 -0
  50. package/esm/projects/revert.js +75 -0
  51. package/esm/projects/verify.js +61 -0
  52. package/esm/resolution/deps.js +526 -0
  53. package/esm/resolution/resolve.js +101 -0
  54. package/esm/utils/debug.js +147 -0
  55. package/esm/utils/target-utils.js +37 -0
  56. package/esm/workspace/paths.js +43 -0
  57. package/esm/workspace/utils.js +31 -0
  58. package/export/export-meta.d.ts +8 -0
  59. package/export/export-meta.js +244 -0
  60. package/export/export-migrations.d.ts +17 -0
  61. package/export/export-migrations.js +187 -0
  62. package/extensions/extensions.d.ts +5 -0
  63. package/extensions/extensions.js +35 -0
  64. package/files/extension/index.d.ts +2 -0
  65. package/files/extension/index.js +19 -0
  66. package/files/extension/reader.d.ts +24 -0
  67. package/files/extension/reader.js +86 -0
  68. package/files/extension/writer.d.ts +39 -0
  69. package/files/extension/writer.js +70 -0
  70. package/files/index.d.ts +5 -0
  71. package/files/index.js +22 -0
  72. package/files/plan/generator.d.ts +22 -0
  73. package/files/plan/generator.js +57 -0
  74. package/files/plan/index.d.ts +4 -0
  75. package/files/plan/index.js +21 -0
  76. package/files/plan/parser.d.ts +27 -0
  77. package/files/plan/parser.js +303 -0
  78. package/files/plan/validators.d.ts +52 -0
  79. package/files/plan/validators.js +187 -0
  80. package/files/plan/writer.d.ts +27 -0
  81. package/files/plan/writer.js +124 -0
  82. package/files/sql/index.d.ts +1 -0
  83. package/files/sql/index.js +17 -0
  84. package/files/sql/writer.d.ts +12 -0
  85. package/files/sql/writer.js +114 -0
  86. package/files/sql-scripts/index.d.ts +1 -0
  87. package/files/sql-scripts/index.js +18 -0
  88. package/files/sql-scripts/reader.d.ts +8 -0
  89. package/files/sql-scripts/reader.js +23 -0
  90. package/files/types/index.d.ts +46 -0
  91. package/files/types/index.js +17 -0
  92. package/files/types/package.d.ts +20 -0
  93. package/files/types/package.js +2 -0
  94. package/index.d.ts +21 -0
  95. package/index.js +45 -0
  96. package/init/client.d.ts +26 -0
  97. package/init/client.js +148 -0
  98. package/init/sql/bootstrap-roles.sql +55 -0
  99. package/init/sql/bootstrap-test-roles.sql +72 -0
  100. package/migrate/clean.d.ts +1 -0
  101. package/migrate/clean.js +27 -0
  102. package/migrate/client.d.ts +80 -0
  103. package/migrate/client.js +555 -0
  104. package/migrate/index.d.ts +5 -0
  105. package/migrate/index.js +21 -0
  106. package/migrate/sql/procedures.sql +258 -0
  107. package/migrate/sql/schema.sql +37 -0
  108. package/migrate/types.d.ts +67 -0
  109. package/migrate/types.js +2 -0
  110. package/migrate/utils/event-logger.d.ts +13 -0
  111. package/migrate/utils/event-logger.js +32 -0
  112. package/migrate/utils/hash.d.ts +12 -0
  113. package/migrate/utils/hash.js +32 -0
  114. package/migrate/utils/transaction.d.ts +27 -0
  115. package/migrate/utils/transaction.js +129 -0
  116. package/modules/modules.d.ts +31 -0
  117. package/modules/modules.js +56 -0
  118. package/package.json +70 -0
  119. package/packaging/package.d.ts +19 -0
  120. package/packaging/package.js +102 -0
  121. package/packaging/transform.d.ts +22 -0
  122. package/packaging/transform.js +75 -0
  123. package/projects/deploy.d.ts +8 -0
  124. package/projects/deploy.js +160 -0
  125. package/projects/revert.d.ts +15 -0
  126. package/projects/revert.js +112 -0
  127. package/projects/verify.d.ts +8 -0
  128. package/projects/verify.js +98 -0
  129. package/resolution/deps.d.ts +57 -0
  130. package/resolution/deps.js +531 -0
  131. package/resolution/resolve.d.ts +37 -0
  132. package/resolution/resolve.js +107 -0
  133. package/utils/debug.d.ts +21 -0
  134. package/utils/debug.js +153 -0
  135. package/utils/target-utils.d.ts +5 -0
  136. package/utils/target-utils.js +40 -0
  137. package/workspace/paths.d.ts +14 -0
  138. package/workspace/paths.js +50 -0
  139. package/workspace/utils.d.ts +8 -0
  140. package/workspace/utils.js +36 -0
@@ -0,0 +1,114 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ /**
4
+ * Write a Sqitch plan file based on the provided rows
5
+ */
6
+ export function writeSqitchPlan(rows, opts) {
7
+ const dir = path.resolve(path.join(opts.outdir, opts.name));
8
+ fs.mkdirSync(dir, { recursive: true });
9
+ const date = () => '2017-08-11T08:11:51Z'; // stubbed timestamp
10
+ const author = opts.author || 'launchql';
11
+ const email = `${author}@5b0c196eeb62`;
12
+ const duplicates = {};
13
+ const plan = opts.replacer(`%syntax-version=1.0.0
14
+ %project=launchql-extension-name
15
+ %uri=launchql-extension-name
16
+
17
+ ${rows
18
+ .map((row) => {
19
+ if (duplicates[row.deploy]) {
20
+ console.log('DUPLICATE ' + row.deploy);
21
+ return '';
22
+ }
23
+ duplicates[row.deploy] = true;
24
+ if (row.deps?.length) {
25
+ return `${row.deploy} [${row.deps.join(' ')}] ${date()} ${author} <${email}> # add ${row.name}`;
26
+ }
27
+ return `${row.deploy} ${date()} ${author} <${email}> # add ${row.name}`;
28
+ })
29
+ .join('\n')}
30
+ `);
31
+ fs.writeFileSync(path.join(dir, 'pgpm.plan'), plan);
32
+ }
33
+ /**
34
+ * Write a plan file with the provided content
35
+ */
36
+ export function writePlanFile(planPath, plan) {
37
+ const content = generatePlanFileContent(plan);
38
+ fs.writeFileSync(planPath, content);
39
+ }
40
+ /**
41
+ * Generate content for a plan file
42
+ */
43
+ export function generatePlanFileContent(plan) {
44
+ const { package: packageName, uri, changes, tags } = plan;
45
+ let content = `%syntax-version=1.0.0\n`;
46
+ content += `%project=${packageName}\n`;
47
+ if (uri) {
48
+ content += `%uri=${uri}\n`;
49
+ }
50
+ content += `\n`;
51
+ // Add changes and their associated tags
52
+ for (const change of changes) {
53
+ content += generateChangeLineContent(change);
54
+ content += `\n`;
55
+ const associatedTags = tags.filter(tag => tag.change === change.name);
56
+ for (const tag of associatedTags) {
57
+ content += generateTagLineContent(tag);
58
+ content += `\n`;
59
+ }
60
+ }
61
+ return content;
62
+ }
63
+ /**
64
+ * Generate a line for a change in a plan file
65
+ */
66
+ export function generateChangeLineContent(change) {
67
+ const { name, dependencies, timestamp, planner, email, comment } = change;
68
+ let line = name;
69
+ // Add dependencies if present
70
+ if (dependencies && dependencies.length > 0) {
71
+ line += ` [${dependencies.join(' ')}]`;
72
+ }
73
+ // Add timestamp if present
74
+ if (timestamp) {
75
+ line += ` ${timestamp}`;
76
+ // Add planner if present
77
+ if (planner) {
78
+ line += ` ${planner}`;
79
+ // Add email if present
80
+ if (email) {
81
+ line += ` <${email}>`;
82
+ }
83
+ }
84
+ }
85
+ // Add comment if present
86
+ if (comment) {
87
+ line += ` # ${comment}`;
88
+ }
89
+ return line;
90
+ }
91
+ /**
92
+ * Generate a line for a tag in a plan file
93
+ */
94
+ export function generateTagLineContent(tag) {
95
+ const { name, timestamp, planner, email, comment } = tag;
96
+ let line = `@${name}`;
97
+ // Add timestamp if present
98
+ if (timestamp) {
99
+ line += ` ${timestamp}`;
100
+ // Add planner if present
101
+ if (planner) {
102
+ line += ` ${planner}`;
103
+ // Add email if present
104
+ if (email) {
105
+ line += ` <${email}>`;
106
+ }
107
+ }
108
+ }
109
+ // Add comment if present
110
+ if (comment) {
111
+ line += ` # ${comment}`;
112
+ }
113
+ return line;
114
+ }
@@ -0,0 +1 @@
1
+ export * from './writer';
@@ -0,0 +1,107 @@
1
+ import { getEnvOptions } from '@pgpmjs/env';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ /**
5
+ * Write SQL files for Sqitch migrations (deploy, revert, verify)
6
+ */
7
+ export const writeSqitchFiles = (rows, opts) => {
8
+ rows.forEach((row) => writeVerify(row, opts));
9
+ rows.forEach((row) => writeRevert(row, opts));
10
+ rows.forEach((row) => writeDeploy(row, opts));
11
+ };
12
+ /**
13
+ * Sort dependencies in a consistent order
14
+ */
15
+ const ordered = (arr) => {
16
+ if (!arr)
17
+ return [];
18
+ return arr.sort((a, b) => a.length - b.length || a.localeCompare(b));
19
+ };
20
+ /**
21
+ * Write a deploy SQL file for a Sqitch change
22
+ */
23
+ const writeDeploy = (row, opts) => {
24
+ const globalOpts = getEnvOptions({
25
+ migrations: {
26
+ codegen: {
27
+ useTx: opts.useTx
28
+ }
29
+ }
30
+ });
31
+ const useTx = globalOpts.migrations.codegen.useTx;
32
+ const deploy = opts.replacer(row.deploy);
33
+ const dir = path.dirname(deploy);
34
+ const prefix = path.join(opts.outdir, opts.name, 'deploy');
35
+ const actualDir = path.resolve(prefix, dir);
36
+ const actualFile = path.resolve(prefix, `${deploy}.sql`);
37
+ fs.mkdirSync(actualDir, { recursive: true });
38
+ const sqlContent = opts.replacer(row.content);
39
+ const content = `-- Deploy: ${deploy}
40
+ -- made with <3 @ constructive.io
41
+
42
+ ${opts.replacer(ordered(row?.deps)
43
+ .map((dep) => `-- requires: ${dep}`)
44
+ .join('\n') || '')}
45
+
46
+ ${useTx ? 'BEGIN;' : ''}
47
+ ${sqlContent}
48
+ ${useTx ? 'COMMIT;' : ''}
49
+ `;
50
+ fs.writeFileSync(actualFile, content);
51
+ };
52
+ /**
53
+ * Write a verify SQL file for a Sqitch change
54
+ */
55
+ const writeVerify = (row, opts) => {
56
+ const globalOpts = getEnvOptions({
57
+ migrations: {
58
+ codegen: {
59
+ useTx: opts.useTx
60
+ }
61
+ }
62
+ });
63
+ const useTx = globalOpts.migrations.codegen.useTx;
64
+ const deploy = opts.replacer(row.deploy);
65
+ const dir = path.dirname(deploy);
66
+ const prefix = path.join(opts.outdir, opts.name, 'verify');
67
+ const actualDir = path.resolve(prefix, dir);
68
+ const actualFile = path.resolve(prefix, `${deploy}.sql`);
69
+ fs.mkdirSync(actualDir, { recursive: true });
70
+ const sqlContent = opts.replacer(row.verify);
71
+ const content = opts.replacer(`-- Verify: ${deploy}
72
+
73
+ ${useTx ? 'BEGIN;' : ''}
74
+ ${sqlContent}
75
+ ${useTx ? 'COMMIT;' : ''}
76
+
77
+ `);
78
+ fs.writeFileSync(actualFile, content);
79
+ };
80
+ /**
81
+ * Write a revert SQL file for a Sqitch change
82
+ */
83
+ const writeRevert = (row, opts) => {
84
+ const globalOpts = getEnvOptions({
85
+ migrations: {
86
+ codegen: {
87
+ useTx: opts.useTx
88
+ }
89
+ }
90
+ });
91
+ const useTx = globalOpts.migrations.codegen.useTx;
92
+ const deploy = opts.replacer(row.deploy);
93
+ const dir = path.dirname(deploy);
94
+ const prefix = path.join(opts.outdir, opts.name, 'revert');
95
+ const actualDir = path.resolve(prefix, dir);
96
+ const actualFile = path.resolve(prefix, `${deploy}.sql`);
97
+ fs.mkdirSync(actualDir, { recursive: true });
98
+ const sqlContent = opts.replacer(row.revert);
99
+ const content = `-- Revert: ${deploy}
100
+
101
+ ${useTx ? 'BEGIN;' : ''}
102
+ ${sqlContent}
103
+ ${useTx ? 'COMMIT;' : ''}
104
+
105
+ `;
106
+ fs.writeFileSync(actualFile, content);
107
+ };
@@ -0,0 +1,2 @@
1
+ // Re-export all sql-scripts functionality
2
+ export * from './reader';
@@ -0,0 +1,19 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ /**
4
+ * Read a SQL script file, return empty string if not found
5
+ */
6
+ export function readScript(basePath, scriptType, changeName) {
7
+ const scriptPath = join(basePath, scriptType, `${changeName}.sql`);
8
+ if (!existsSync(scriptPath)) {
9
+ return '';
10
+ }
11
+ return readFileSync(scriptPath, 'utf-8');
12
+ }
13
+ /**
14
+ * Check if a script file exists
15
+ */
16
+ export function scriptExists(basePath, scriptType, changeName) {
17
+ const scriptPath = join(basePath, scriptType, `${changeName}.sql`);
18
+ return existsSync(scriptPath);
19
+ }
@@ -0,0 +1 @@
1
+ export * from './package';
@@ -0,0 +1 @@
1
+ export {};
package/esm/index.js ADDED
@@ -0,0 +1,21 @@
1
+ export * from './core/class/pgpm';
2
+ export * from './export/export-meta';
3
+ export * from './export/export-migrations';
4
+ export * from './extensions/extensions';
5
+ export * from './modules/modules';
6
+ export * from './packaging/package';
7
+ export * from './packaging/transform';
8
+ export * from './resolution/deps';
9
+ export * from './resolution/resolve';
10
+ export * from './workspace/paths';
11
+ export * from './workspace/utils';
12
+ export * from './core/template-scaffold';
13
+ export * from './core/boilerplate-types';
14
+ export * from './core/boilerplate-scanner';
15
+ // Export package-files functionality (now integrated into core)
16
+ export * from './files';
17
+ export { cleanSql } from './migrate/clean';
18
+ export { PgpmMigrate } from './migrate/client';
19
+ export { PgpmInit } from './init/client';
20
+ export { hashFile, hashString } from './migrate/utils/hash';
21
+ export { executeQuery, withTransaction } from './migrate/utils/transaction';
@@ -0,0 +1,144 @@
1
+ import { Logger } from '@pgpmjs/logger';
2
+ import { readFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { getPgPool } from 'pg-cache';
5
+ const log = new Logger('init');
6
+ export class PgpmInit {
7
+ pool;
8
+ pgConfig;
9
+ constructor(config) {
10
+ this.pgConfig = config;
11
+ this.pool = getPgPool(this.pgConfig);
12
+ }
13
+ /**
14
+ * Bootstrap standard roles (anonymous, authenticated, administrator)
15
+ */
16
+ async bootstrapRoles() {
17
+ try {
18
+ log.info('Bootstrapping PGPM roles...');
19
+ const sqlPath = join(__dirname, 'sql', 'bootstrap-roles.sql');
20
+ const sql = readFileSync(sqlPath, 'utf-8');
21
+ await this.pool.query(sql);
22
+ log.success('Successfully bootstrapped PGPM roles');
23
+ }
24
+ catch (error) {
25
+ log.error('Failed to bootstrap roles:', error);
26
+ throw error;
27
+ }
28
+ }
29
+ /**
30
+ * Bootstrap test roles (roles only, no users)
31
+ */
32
+ async bootstrapTestRoles() {
33
+ try {
34
+ log.warn('WARNING: This command creates test roles and should NEVER be run on a production database!');
35
+ log.info('Bootstrapping PGPM test roles...');
36
+ const sqlPath = join(__dirname, 'sql', 'bootstrap-test-roles.sql');
37
+ const sql = readFileSync(sqlPath, 'utf-8');
38
+ await this.pool.query(sql);
39
+ log.success('Successfully bootstrapped PGPM test roles');
40
+ }
41
+ catch (error) {
42
+ log.error('Failed to bootstrap test roles:', error);
43
+ throw error;
44
+ }
45
+ }
46
+ /**
47
+ * Bootstrap database roles with custom username and password
48
+ */
49
+ async bootstrapDbRoles(username, password) {
50
+ try {
51
+ log.info(`Bootstrapping PGPM database roles for user: ${username}...`);
52
+ const sql = `
53
+ BEGIN;
54
+ DO $do$
55
+ DECLARE
56
+ v_username TEXT := '${username.replace(/'/g, "''")}';
57
+ v_password TEXT := '${password.replace(/'/g, "''")}';
58
+ BEGIN
59
+ BEGIN
60
+ EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', v_username, v_password);
61
+ EXCEPTION
62
+ WHEN duplicate_object THEN
63
+ -- Role already exists; optionally sync attributes here with ALTER ROLE
64
+ NULL;
65
+ END;
66
+ END
67
+ $do$;
68
+
69
+ -- Robust GRANTs under concurrency: GRANT can race on pg_auth_members unique index.
70
+ -- Catch unique_violation (23505) and continue so CI/CD concurrent jobs don't fail.
71
+ DO $do$
72
+ DECLARE
73
+ v_username TEXT := '${username.replace(/'/g, "''")}';
74
+ BEGIN
75
+ BEGIN
76
+ EXECUTE format('GRANT %I TO %I', 'anonymous', v_username);
77
+ EXCEPTION
78
+ WHEN unique_violation THEN
79
+ -- Membership was granted concurrently; ignore.
80
+ NULL;
81
+ WHEN undefined_object THEN
82
+ -- One of the roles doesn't exist yet; order operations as needed.
83
+ RAISE NOTICE 'Missing role when granting % to %', 'anonymous', v_username;
84
+ END;
85
+
86
+ BEGIN
87
+ EXECUTE format('GRANT %I TO %I', 'authenticated', v_username);
88
+ EXCEPTION
89
+ WHEN unique_violation THEN
90
+ -- Membership was granted concurrently; ignore.
91
+ NULL;
92
+ WHEN undefined_object THEN
93
+ RAISE NOTICE 'Missing role when granting % to %', 'authenticated', v_username;
94
+ END;
95
+ END
96
+ $do$;
97
+ COMMIT;
98
+ `;
99
+ await this.pool.query(sql);
100
+ log.success(`Successfully bootstrapped PGPM database roles for user: ${username}`);
101
+ }
102
+ catch (error) {
103
+ log.error(`Failed to bootstrap database roles for user ${username}:`, error);
104
+ throw error;
105
+ }
106
+ }
107
+ /**
108
+ * Remove database roles and revoke grants
109
+ */
110
+ async removeDbRoles(username) {
111
+ try {
112
+ log.info(`Removing PGPM database roles for user: ${username}...`);
113
+ const sql = `
114
+ BEGIN;
115
+ DO $do$
116
+ BEGIN
117
+ IF EXISTS (
118
+ SELECT 1
119
+ FROM
120
+ pg_catalog.pg_roles
121
+ WHERE
122
+ rolname = '${username}') THEN
123
+ REVOKE anonymous FROM ${username};
124
+ REVOKE authenticated FROM ${username};
125
+ DROP ROLE ${username};
126
+ END IF;
127
+ END
128
+ $do$;
129
+ COMMIT;
130
+ `;
131
+ await this.pool.query(sql);
132
+ log.success(`Successfully removed PGPM database roles for user: ${username}`);
133
+ }
134
+ catch (error) {
135
+ log.error(`Failed to remove database roles for user ${username}:`, error);
136
+ throw error;
137
+ }
138
+ }
139
+ /**
140
+ * Close the database connection
141
+ */
142
+ async close() {
143
+ }
144
+ }
@@ -0,0 +1,55 @@
1
+ BEGIN;
2
+ DO $do$
3
+ BEGIN
4
+ -- anonymous
5
+ BEGIN
6
+ EXECUTE format('CREATE ROLE %I', 'anonymous');
7
+ EXCEPTION
8
+ WHEN duplicate_object THEN
9
+ -- Role already exists; optionally sync attributes here with ALTER ROLE
10
+ NULL;
11
+ END;
12
+
13
+ -- authenticated
14
+ BEGIN
15
+ EXECUTE format('CREATE ROLE %I', 'authenticated');
16
+ EXCEPTION
17
+ WHEN duplicate_object THEN
18
+ -- Role already exists; optionally sync attributes here with ALTER ROLE
19
+ NULL;
20
+ END;
21
+
22
+ -- administrator
23
+ BEGIN
24
+ EXECUTE format('CREATE ROLE %I', 'administrator');
25
+ EXCEPTION
26
+ WHEN duplicate_object THEN
27
+ -- Role already exists; optionally sync attributes here with ALTER ROLE
28
+ NULL;
29
+ END;
30
+ END
31
+ $do$;
32
+
33
+ -- Set role attributes (safe to run even if role already exists)
34
+ ALTER USER anonymous WITH NOCREATEDB;
35
+ ALTER USER anonymous WITH NOSUPERUSER;
36
+ ALTER USER anonymous WITH NOCREATEROLE;
37
+ ALTER USER anonymous WITH NOLOGIN;
38
+ ALTER USER anonymous WITH NOREPLICATION;
39
+ ALTER USER anonymous WITH NOBYPASSRLS;
40
+
41
+ ALTER USER authenticated WITH NOCREATEDB;
42
+ ALTER USER authenticated WITH NOSUPERUSER;
43
+ ALTER USER authenticated WITH NOCREATEROLE;
44
+ ALTER USER authenticated WITH NOLOGIN;
45
+ ALTER USER authenticated WITH NOREPLICATION;
46
+ ALTER USER authenticated WITH NOBYPASSRLS;
47
+
48
+ ALTER USER administrator WITH NOCREATEDB;
49
+ ALTER USER administrator WITH NOSUPERUSER;
50
+ ALTER USER administrator WITH NOCREATEROLE;
51
+ ALTER USER administrator WITH NOLOGIN;
52
+ ALTER USER administrator WITH NOREPLICATION;
53
+ -- they CAN bypass RLS
54
+ ALTER USER administrator WITH BYPASSRLS;
55
+ COMMIT;
@@ -0,0 +1,72 @@
1
+ BEGIN;
2
+ DO $do$
3
+ BEGIN
4
+ BEGIN
5
+ EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', 'app_user', 'app_password');
6
+ EXCEPTION
7
+ WHEN duplicate_object THEN
8
+ -- Role already exists; optionally sync attributes here with ALTER ROLE
9
+ NULL;
10
+ END;
11
+
12
+ BEGIN
13
+ EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', 'app_admin', 'admin_password');
14
+ EXCEPTION
15
+ WHEN duplicate_object THEN
16
+ -- Role already exists; optionally sync attributes here with ALTER ROLE
17
+ NULL;
18
+ END;
19
+ END
20
+ $do$;
21
+
22
+ DO $do$
23
+ BEGIN
24
+ BEGIN
25
+ EXECUTE format('GRANT %I TO %I', 'anonymous', 'app_user');
26
+ EXCEPTION
27
+ WHEN unique_violation THEN
28
+ -- Membership was granted concurrently; ignore.
29
+ NULL;
30
+ WHEN undefined_object THEN
31
+ -- One of the roles doesn't exist yet; order operations as needed.
32
+ RAISE NOTICE 'Missing role when granting % to %', 'anonymous', 'app_user';
33
+ END;
34
+
35
+ BEGIN
36
+ EXECUTE format('GRANT %I TO %I', 'authenticated', 'app_user');
37
+ EXCEPTION
38
+ WHEN unique_violation THEN
39
+ NULL;
40
+ WHEN undefined_object THEN
41
+ RAISE NOTICE 'Missing role when granting % to %', 'authenticated', 'app_user';
42
+ END;
43
+
44
+ BEGIN
45
+ EXECUTE format('GRANT %I TO %I', 'anonymous', 'administrator');
46
+ EXCEPTION
47
+ WHEN unique_violation THEN
48
+ NULL;
49
+ WHEN undefined_object THEN
50
+ RAISE NOTICE 'Missing role when granting % to %', 'anonymous', 'administrator';
51
+ END;
52
+
53
+ BEGIN
54
+ EXECUTE format('GRANT %I TO %I', 'authenticated', 'administrator');
55
+ EXCEPTION
56
+ WHEN unique_violation THEN
57
+ NULL;
58
+ WHEN undefined_object THEN
59
+ RAISE NOTICE 'Missing role when granting % to %', 'authenticated', 'administrator';
60
+ END;
61
+
62
+ BEGIN
63
+ EXECUTE format('GRANT %I TO %I', 'administrator', 'app_admin');
64
+ EXCEPTION
65
+ WHEN unique_violation THEN
66
+ NULL;
67
+ WHEN undefined_object THEN
68
+ RAISE NOTICE 'Missing role when granting % to %', 'administrator', 'app_admin';
69
+ END;
70
+ END
71
+ $do$;
72
+ COMMIT;
@@ -0,0 +1,23 @@
1
+ import { deparse, parse } from 'pgsql-parser';
2
+ const filterStatements = (stmts) => {
3
+ const filteredStmts = stmts.filter(node => {
4
+ const stmt = node.stmt;
5
+ return stmt && !stmt.hasOwnProperty('TransactionStmt') &&
6
+ !stmt.hasOwnProperty('CreateExtensionStmt');
7
+ });
8
+ const hasFiltered = filteredStmts.length !== stmts.length;
9
+ return { filteredStmts, hasFiltered };
10
+ };
11
+ export const cleanSql = async (sql, pretty, functionDelimiter) => {
12
+ const parsed = await parse(sql);
13
+ const { filteredStmts, hasFiltered } = filterStatements(parsed.stmts);
14
+ if (!hasFiltered) {
15
+ return sql;
16
+ }
17
+ parsed.stmts = filteredStmts;
18
+ const finalSql = await deparse(parsed, {
19
+ pretty,
20
+ functionDelimiter
21
+ });
22
+ return finalSql;
23
+ };