@prairielearn/postgres-tools 2.0.12 → 2.0.14
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/CHANGELOG.md +18 -0
- package/dist/bin/pg-describe.js +4 -5
- package/dist/bin/pg-describe.js.map +1 -1
- package/dist/bin/pg-diff.js +1 -1
- package/dist/bin/pg-diff.js.map +1 -1
- package/dist/describe.js +1 -1
- package/dist/describe.js.map +1 -1
- package/dist/diff.js +11 -11
- package/dist/diff.js.map +1 -1
- package/package.json +18 -18
- package/src/bin/pg-describe.ts +5 -6
- package/src/bin/pg-diff.ts +1 -1
- package/src/describe.ts +1 -1
- package/src/diff.ts +29 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @prairielearn/postgres-tools
|
|
2
2
|
|
|
3
|
+
## 2.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c24120e: Minor changes to reduce dependency on lodash
|
|
8
|
+
- 03f1008: Upgrade all JavaScript dependencies
|
|
9
|
+
- Updated dependencies [c24120e]
|
|
10
|
+
- Updated dependencies [03f1008]
|
|
11
|
+
- @prairielearn/postgres@2.1.10
|
|
12
|
+
|
|
13
|
+
## 2.0.13
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 984dc62: Upgrade all JavaScript dependencies
|
|
18
|
+
- Updated dependencies [984dc62]
|
|
19
|
+
- @prairielearn/postgres@2.1.9
|
|
20
|
+
|
|
3
21
|
## 2.0.12
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/bin/pg-describe.js
CHANGED
|
@@ -3,7 +3,6 @@ import path from 'path';
|
|
|
3
3
|
import async from 'async';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
|
-
import _ from 'lodash';
|
|
7
6
|
import yargs from 'yargs';
|
|
8
7
|
import { describeDatabase, formatDatabaseDescription, } from '../describe.js';
|
|
9
8
|
const args = yargs(process.argv.slice(2))
|
|
@@ -64,16 +63,16 @@ describeDatabase(argv._[0].toString(), options).then(async (description) => {
|
|
|
64
63
|
});
|
|
65
64
|
function printDescription(description) {
|
|
66
65
|
const formattedDescription = formatDatabaseDescription(description, { coloredOutput });
|
|
67
|
-
|
|
66
|
+
for (const tableName of Object.keys(formattedDescription.tables).sort()) {
|
|
68
67
|
process.stdout.write(formatText(`[table] ${tableName}\n`, chalk.bold));
|
|
69
68
|
process.stdout.write(formattedDescription.tables[tableName]);
|
|
70
69
|
process.stdout.write('\n\n');
|
|
71
|
-
}
|
|
72
|
-
|
|
70
|
+
}
|
|
71
|
+
for (const enumName of Object.keys(formattedDescription.enums).sort()) {
|
|
73
72
|
process.stdout.write(formatText(`[enum] ${enumName}\n`, chalk.bold));
|
|
74
73
|
process.stdout.write(formattedDescription.enums[enumName]);
|
|
75
74
|
process.stdout.write('\n\n');
|
|
76
|
-
}
|
|
75
|
+
}
|
|
77
76
|
}
|
|
78
77
|
async function writeDescriptionToDisk(description, dir) {
|
|
79
78
|
const formattedDescription = formatDatabaseDescription(description, { coloredOutput: false });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-describe.js","sourceRoot":"","sources":["../../src/bin/pg-describe.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"pg-describe.js","sourceRoot":"","sources":["../../src/bin/pg-describe.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport path from 'path';\n\nimport async from 'async';\nimport chalk from 'chalk';\nimport fs from 'fs-extra';\nimport yargs from 'yargs';\n\nimport {\n type DatabaseDescription,\n describeDatabase,\n formatDatabaseDescription,\n} from '../describe.js';\n\nconst args = yargs(process.argv.slice(2))\n .usage('Usage: $0 <database name> [options]')\n .demandCommand(1)\n .option('output', {\n alias: 'o',\n nargs: 1,\n string: true,\n description: 'Specify a directory to output files to',\n })\n .option('ignore-tables', {\n array: true,\n description: 'a list of tables to ignore',\n })\n .option('ignore-enums', {\n array: true,\n description: 'a list of enums to ignore',\n })\n .option('ignore-columns', {\n array: true,\n description: 'a list of columns to ignore, formatted like [table].[column]',\n })\n .help('h')\n .alias('h', 'help')\n .example('$0 postgres', 'Describe the \"postgres\" database')\n .example(\n '$0 userdb -o db_description --ignore-tables a b --ignore-columns a.col1 a.col2',\n 'Describe the \"userdb\" database; ignore specific tables and columns',\n )\n .strict();\n\nconst argv = args.parseSync();\n\nif (argv._.length !== 1) {\n args.showHelp();\n process.exit(1);\n}\n\n// Disable color if we're not attached to a tty\nconst coloredOutput = !argv.output && process.stdout.isTTY;\n\nconst options = {\n ignoreTables: (argv['ignore-tables'] ?? []).map((table) => table.toString()),\n ignoreEnums: (argv['ignore-enums'] ?? []).map((enumName) => enumName.toString()),\n ignoreColumns: (argv['ignore-columns'] ?? []).map((column) => column.toString()),\n};\n\nfunction formatText(text: string, formatter: (text: string) => string) {\n if (!argv.output && coloredOutput) {\n return formatter(text);\n }\n return text;\n}\n\ndescribeDatabase(argv._[0].toString(), options).then(\n async (description) => {\n if (argv.output) {\n await writeDescriptionToDisk(description, argv.output);\n } else {\n printDescription(description);\n }\n process.exit(0);\n },\n (err) => {\n console.error(err);\n process.exit(1);\n },\n);\n\nfunction printDescription(description: DatabaseDescription) {\n const formattedDescription = formatDatabaseDescription(description, { coloredOutput });\n for (const tableName of Object.keys(formattedDescription.tables).sort()) {\n process.stdout.write(formatText(`[table] ${tableName}\\n`, chalk.bold));\n process.stdout.write(formattedDescription.tables[tableName]);\n process.stdout.write('\\n\\n');\n }\n\n for (const enumName of Object.keys(formattedDescription.enums).sort()) {\n process.stdout.write(formatText(`[enum] ${enumName}\\n`, chalk.bold));\n process.stdout.write(formattedDescription.enums[enumName]);\n process.stdout.write('\\n\\n');\n }\n}\n\nasync function writeDescriptionToDisk(description: DatabaseDescription, dir: string) {\n const formattedDescription = formatDatabaseDescription(description, { coloredOutput: false });\n await fs.emptyDir(dir);\n await fs.mkdir(path.join(dir, 'tables'));\n await fs.mkdir(path.join(dir, 'enums'));\n await async.eachOf(formattedDescription.tables, async (value, key) => {\n await fs.writeFile(path.join(dir, 'tables', `${key}.pg`), value);\n });\n await async.eachOf(formattedDescription.enums, async (value, key) => {\n await fs.writeFile(path.join(dir, 'enums', `${key}.pg`), value);\n });\n}\n"]}
|
package/dist/bin/pg-diff.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import yargs from 'yargs';
|
|
3
|
-
import { diffDatabases, diffDirectories,
|
|
3
|
+
import { diffDatabaseAndDirectory, diffDatabases, diffDirectories, diffDirectoryAndDatabase, } from '../diff.js';
|
|
4
4
|
const args = yargs(process.argv.slice(2))
|
|
5
5
|
.usage('Usage: $0 [options]')
|
|
6
6
|
.option('db', {
|
package/dist/bin/pg-diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-diff.js","sourceRoot":"","sources":["../../src/bin/pg-diff.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,aAAa,EACb,eAAe,EACf,wBAAwB,
|
|
1
|
+
{"version":3,"file":"pg-diff.js","sourceRoot":"","sources":["../../src/bin/pg-diff.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport yargs from 'yargs';\n\nimport {\n diffDatabaseAndDirectory,\n diffDatabases,\n diffDirectories,\n diffDirectoryAndDatabase,\n} from '../diff.js';\n\nconst args = yargs(process.argv.slice(2))\n .usage('Usage: $0 [options]')\n .option('db', {\n description: 'reads a description from the named database',\n })\n .option('dir', {\n description: \"reads a description from a directory that's been generated by pg-describe\",\n })\n .help('h')\n .alias('h', 'help')\n .example(\n '$0 --db postgres --dir db_dump',\n 'Diffs the database \"postgres\" with the description in the directory \"db_dump\"',\n )\n .example(\n '$0 --db postgres --db old_restore',\n 'Diffs the database \"postgres\" with the database \"old_restore\"',\n )\n .strict();\n\nconst argv = args.parseSync();\n\nconst options = {\n outputFormat: 'string',\n coloredOutput: process.stdout.isTTY,\n};\n\nif (argv.db && typeof argv.db === 'string' && argv.dir && typeof argv.dir === 'string') {\n // Ensure correct ordering for the sake of diffs\n if (process.argv.indexOf('--db') < process.argv.indexOf('--dir')) {\n diffDatabaseAndDirectory(argv.db, argv.dir, options).then(handleResults, handleError);\n } else {\n diffDirectoryAndDatabase(argv.dir, argv.db, options).then(handleResults, handleError);\n }\n} else if (argv.db && Array.isArray(argv.db) && argv.db.length === 2 && !argv.dir) {\n diffDatabases(argv.db[0], argv.db[1], options).then(handleResults, handleError);\n} else if (argv.dir && Array.isArray(argv.dir) && argv.dir.length === 2 && !argv.db) {\n diffDirectories(argv.dir[0], argv.dir[1], options).then(handleResults, handleError);\n} else {\n args.showHelp();\n process.exit(1);\n}\n\nfunction handleResults(results: string) {\n process.stdout.write(results);\n process.exit(0);\n}\n\nfunction handleError(err: any) {\n console.error(err);\n process.exit(1);\n}\n"]}
|
package/dist/describe.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { parse as parsePostgresArray } from 'postgres-array';
|
|
3
|
-
import {
|
|
3
|
+
import { PostgresPool, loadSqlEquiv } from '@prairielearn/postgres';
|
|
4
4
|
const sql = loadSqlEquiv(import.meta.url);
|
|
5
5
|
async function describeWithPool(pool, options) {
|
|
6
6
|
const ignoreTables = options?.ignoreTables || [];
|
package/dist/describe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from 'chalk';\nimport { parse as parsePostgresArray } from 'postgres-array';\n\nimport { loadSqlEquiv, PostgresPool } from '@prairielearn/postgres';\n\nconst sql = loadSqlEquiv(import.meta.url);\n\ninterface ColumnDescription {\n name: string;\n type: string;\n notnull: boolean;\n default: any;\n}\n\ninterface IndexDescription {\n name: string;\n isprimary: boolean;\n isunique: boolean;\n indexdef: string;\n constraintdef: string;\n contype: string;\n}\n\ninterface ForeignKeyConstraintDescription {\n name: string;\n def: string;\n}\n\ninterface ReferenceDescription {\n name: string;\n table: string;\n condef: string;\n}\n\ninterface CheckConstraintDescription {\n name: string;\n def: string;\n}\n\ninterface TableDescription {\n columns: ColumnDescription[];\n indexes: IndexDescription[];\n foreignKeyConstraints: ForeignKeyConstraintDescription[];\n references: ReferenceDescription[];\n checkConstraints: CheckConstraintDescription[];\n}\n\nexport interface DatabaseDescription {\n tables: Record<string, TableDescription>;\n enums: Record<string, string[]>;\n}\n\ninterface DescribeOptions {\n ignoreTables?: string[];\n ignoreColumns?: string[];\n ignoreEnums?: string[];\n}\n\nasync function describeWithPool(\n pool: PostgresPool,\n options: DescribeOptions,\n): Promise<DatabaseDescription> {\n const ignoreTables = options?.ignoreTables || [];\n const ignoreEnums = options?.ignoreEnums || [];\n let ignoreColumns: Record<string, string[]> = {};\n\n const output: DatabaseDescription = {\n tables: {},\n enums: {},\n };\n\n // Get the names of the tables and filter out any ignored tables\n const tablesRes = await pool.queryAsync(sql.get_tables, []);\n const tables = tablesRes.rows.filter((table) => ignoreTables.indexOf(table.name) === -1);\n\n // Transform ignored columns into a map from table names to arrays\n // of column names\n if (options.ignoreColumns && Array.isArray(options.ignoreColumns)) {\n ignoreColumns = options.ignoreColumns\n .filter((ignore) => {\n return /^[^\\s.]*\\.[^\\s.]*$/.test(ignore);\n })\n .reduce(\n (result, value) => {\n const res = /^(([^\\s.]*)\\.([^\\s.]*))$/.exec(value);\n if (!res) {\n throw new Error(`Invalid ignore column: ${value}`);\n }\n const table = res[2];\n const column = res[3];\n (result[table] || (result[table] = [])).push(column);\n return result;\n },\n {} as Record<string, string[]>,\n );\n }\n\n // Get column info for each table\n for (const table of tables) {\n const columnResults = await pool.queryAsync(sql.get_columns_for_table, {\n oid: table.oid,\n });\n\n const columns = columnResults.rows.filter((row) => {\n return (ignoreColumns[table.name] || []).indexOf(row.name) === -1;\n });\n\n const indexResults = await pool.queryAsync(sql.get_indexes_for_table, {\n oid: table.oid,\n });\n\n const foreignKeyConstraintResults = await pool.queryAsync(\n sql.get_foreign_key_constraints_for_table,\n {\n oid: table.oid,\n },\n );\n\n const referenceResults = await pool.queryAsync(sql.get_references_for_table, {\n oid: table.oid,\n });\n\n // Filter out references from ignored tables\n const references = referenceResults.rows.filter((row) => {\n return ignoreTables.indexOf(row.table) === -1;\n });\n\n const checkConstraintResults = await pool.queryAsync(sql.get_check_constraints_for_table, {\n oid: table.oid,\n });\n\n output.tables[table.name] = {\n columns,\n indexes: indexResults.rows,\n foreignKeyConstraints: foreignKeyConstraintResults.rows,\n references,\n checkConstraints: checkConstraintResults.rows,\n };\n }\n\n // Get all enums\n const enumsRes = await pool.queryAsync(sql.get_enums, []);\n\n // Filter ignored enums\n const rows = enumsRes.rows.filter((row) => {\n return ignoreEnums.indexOf(row.name) === -1;\n });\n\n rows.forEach((row) => {\n output.enums[row.name] = parsePostgresArray(row.values);\n });\n\n return output;\n}\n\n/**\n * Will produce a description of a given database's schema. This will include\n * information about tables, enums, constraints, indices, etc.\n */\nexport async function describeDatabase(\n databaseName: string,\n options: DescribeOptions = {},\n): Promise<DatabaseDescription> {\n // Connect to the database.\n const pool = new PostgresPool();\n const pgConfig = {\n user: 'postgres',\n database: databaseName,\n host: 'localhost',\n max: 10,\n idleTimeoutMillis: 30000,\n };\n function idleErrorHandler(err: Error) {\n throw err;\n }\n await pool.initAsync(pgConfig, idleErrorHandler);\n\n try {\n return await describeWithPool(pool, options);\n } finally {\n await pool.closeAsync();\n }\n}\n\nexport function formatDatabaseDescription(\n description: DatabaseDescription,\n options = { coloredOutput: true },\n): { tables: Record<string, string>; enums: Record<string, string> } {\n const output = {\n tables: {} as Record<string, string>,\n enums: {} as Record<string, string>,\n };\n\n Object.keys(description.tables).forEach((tableName) => (output.tables[tableName] = ''));\n\n /**\n * Optionally applies the given formatter to the text if colored output is\n * enabled.\n */\n function formatText(text: string, formatter: (s: string) => string): string {\n if (options.coloredOutput) {\n return formatter(text);\n }\n return text;\n }\n\n Object.entries(description.tables).forEach(([tableName, table]) => {\n if (table.columns.length > 0) {\n output.tables[tableName] += formatText('columns\\n', chalk.underline);\n output.tables[tableName] += table.columns\n .map((row) => {\n let rowText = formatText(` ${row.name}`, chalk.bold);\n rowText += ':' + formatText(` ${row.type}`, chalk.green);\n if (row.notnull) {\n rowText += formatText(' not null', chalk.gray);\n }\n if (row.default) {\n rowText += formatText(` default ${row.default}`, chalk.gray);\n }\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.indexes.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('indexes\\n', chalk.underline);\n output.tables[tableName] += table.indexes\n .map((row) => {\n const using = row.indexdef.substring(row.indexdef.indexOf('USING '));\n let rowText = formatText(` ${row.name}`, chalk.bold) + ':';\n // Primary indexes are implicitly unique, so we don't need to\n // capture that explicitly.\n if (row.isunique && !row.isprimary) {\n if (!row.constraintdef || row.constraintdef.indexOf('UNIQUE') === -1) {\n // Some unique indexes don't include the UNIQUE constraint\n // as part of the constraint definition, so we need to capture\n // that manually.\n rowText += formatText(' UNIQUE', chalk.green);\n }\n }\n rowText += row.constraintdef ? formatText(` ${row.constraintdef}`, chalk.green) : '';\n rowText += using ? formatText(` ${using}`, chalk.green) : '';\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.checkConstraints.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('check constraints\\n', chalk.underline);\n output.tables[tableName] += table.checkConstraints\n .map((row) => {\n // Particularly long constraints are formatted as multiple lines.\n // We'll collapse them into a single line for better appearance in\n // the resulting file.\n //\n // The first replace handles lines that end with a parenthesis: we\n // want to avoid spaces between the parenthesis and the next token.\n //\n // The second replace handles all other lines: we want to collapse\n // all leading whitespace into a single space.\n const def = row.def.replace(/\\(\\n/g, '(').replace(/\\n\\s*/g, ' ');\n\n let rowText = formatText(` ${row.name}:`, chalk.bold);\n rowText += formatText(` ${def}`, chalk.green);\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.foreignKeyConstraints.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('foreign-key constraints\\n', chalk.underline);\n output.tables[tableName] += table.foreignKeyConstraints\n .map((row) => {\n let rowText = formatText(` ${row.name}:`, chalk.bold);\n rowText += formatText(` ${row.def}`, chalk.green);\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.references.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('referenced by\\n', chalk.underline);\n output.tables[tableName] += table.references\n ?.map((row) => {\n let rowText = formatText(` ${row.table}:`, chalk.bold);\n rowText += formatText(` ${row.condef}`, chalk.green);\n return rowText;\n })\n .join('\\n');\n }\n });\n\n Object.entries(description.enums).forEach(([enumName, enumValues]) => {\n output.enums[enumName] = formatText(enumValues.join(', '), chalk.gray);\n });\n\n // We need to tack on a newline to everything.\n Object.entries(output.tables).forEach(([tableName, table]) => {\n output.tables[tableName] = table + '\\n';\n });\n Object.entries(output.enums).forEach(([enumName, enumValues]) => {\n output.enums[enumName] = enumValues + '\\n';\n });\n\n return output;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,IAAI,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAqD1C,KAAK,UAAU,gBAAgB,CAC7B,IAAkB,EAClB,OAAwB;IAExB,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,EAAE,CAAC;IACjD,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,EAAE,CAAC;IAC/C,IAAI,aAAa,GAA6B,EAAE,CAAC;IAEjD,MAAM,MAAM,GAAwB;QAClC,MAAM,EAAE,EAAE;QACV,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,gEAAgE;IAChE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzF,kEAAkE;IAClE,kBAAkB;IAClB,IAAI,OAAO,CAAC,aAAa,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAClE,aAAa,GAAG,OAAO,CAAC,aAAa;aAClC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;YACjB,OAAO,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC,CAAC;aACD,MAAM,CACL,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YAChB,MAAM,GAAG,GAAG,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YACrB,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YACtB,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrD,OAAO,MAAM,CAAC;QAChB,CAAC,EACD,EAA8B,CAC/B,CAAC;IACN,CAAC;IAED,iCAAiC;IACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,qBAAqB,EAAE;YACrE,GAAG,EAAE,KAAK,CAAC,GAAG;SACf,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YAChD,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,qBAAqB,EAAE;YACpE,GAAG,EAAE,KAAK,CAAC,GAAG;SACf,CAAC,CAAC;QAEH,MAAM,2BAA2B,GAAG,MAAM,IAAI,CAAC,UAAU,CACvD,GAAG,CAAC,qCAAqC,EACzC;YACE,GAAG,EAAE,KAAK,CAAC,GAAG;SACf,CACF,CAAC;QAEF,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,wBAAwB,EAAE;YAC3E,GAAG,EAAE,KAAK,CAAC,GAAG;SACf,CAAC,CAAC;QAEH,4CAA4C;QAC5C,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YACtD,OAAO,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,MAAM,sBAAsB,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,+BAA+B,EAAE;YACxF,GAAG,EAAE,KAAK,CAAC,GAAG;SACf,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;YAC1B,OAAO;YACP,OAAO,EAAE,YAAY,CAAC,IAAI;YAC1B,qBAAqB,EAAE,2BAA2B,CAAC,IAAI;YACvD,UAAU;YACV,gBAAgB,EAAE,sBAAsB,CAAC,IAAI;SAC9C,CAAC;IACJ,CAAC;IAED,gBAAgB;IAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAE1D,uBAAuB;IACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QACxC,OAAO,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,YAAoB,EACpB,UAA2B,EAAE;IAE7B,2BAA2B;IAC3B,MAAM,IAAI,GAAG,IAAI,YAAY,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG;QACf,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,YAAY;QACtB,IAAI,EAAE,WAAW;QACjB,GAAG,EAAE,EAAE;QACP,iBAAiB,EAAE,KAAK;KACzB,CAAC;IACF,SAAS,gBAAgB,CAAC,GAAU;QAClC,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,OAAO,MAAM,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,WAAgC,EAChC,OAAO,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE;IAEjC,MAAM,MAAM,GAAG;QACb,MAAM,EAAE,EAA4B;QACpC,KAAK,EAAE,EAA4B;KACpC,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAExF;;;OAGG;IACH,SAAS,UAAU,CAAC,IAAY,EAAE,SAAgC;QAChE,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAC1B,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE;QAChE,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YACrE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,OAAO;iBACtC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACX,IAAI,OAAO,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBACxD,OAAO,IAAI,GAAG,GAAG,UAAU,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBACzD,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;oBAChB,OAAO,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBACjD,CAAC;gBACD,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;oBAChB,OAAO,IAAI,UAAU,CAAC,YAAY,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC/D,CAAC;gBACD,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC;YACrC,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YACrE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,OAAO;iBACtC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACX,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACrE,IAAI,OAAO,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;gBAC9D,6DAA6D;gBAC7D,2BAA2B;gBAC3B,IAAI,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;oBACnC,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;wBACrE,0DAA0D;wBAC1D,8DAA8D;wBAC9D,iBAAiB;wBACjB,OAAO,IAAI,UAAU,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBAChD,CAAC;gBACH,CAAC;gBACD,OAAO,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,aAAa,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrF,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7D,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,IAAI,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtC,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC;YACrC,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,qBAAqB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YAC/E,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,gBAAgB;iBAC/C,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACX,iEAAiE;gBACjE,kEAAkE;gBAClE,sBAAsB;gBACtB,EAAE;gBACF,kEAAkE;gBAClE,mEAAmE;gBACnE,EAAE;gBACF,kEAAkE;gBAClE,8CAA8C;gBAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;gBAEjE,IAAI,OAAO,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBACzD,OAAO,IAAI,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC9C,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,IAAI,KAAK,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3C,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC;YACrC,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,2BAA2B,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YACrF,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,qBAAqB;iBACpD,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACX,IAAI,OAAO,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBACzD,OAAO,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAClD,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC;YACrC,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YAC3E,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,UAAU;gBAC1C,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACZ,IAAI,OAAO,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC1D,OAAO,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBACrD,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,EAAE;QACnE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,8CAA8C;IAC9C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE;QAC3D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC;IAC1C,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,EAAE;QAC9D,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import chalk from 'chalk';\nimport { parse as parsePostgresArray } from 'postgres-array';\n\nimport { PostgresPool, loadSqlEquiv } from '@prairielearn/postgres';\n\nconst sql = loadSqlEquiv(import.meta.url);\n\ninterface ColumnDescription {\n name: string;\n type: string;\n notnull: boolean;\n default: any;\n}\n\ninterface IndexDescription {\n name: string;\n isprimary: boolean;\n isunique: boolean;\n indexdef: string;\n constraintdef: string;\n contype: string;\n}\n\ninterface ForeignKeyConstraintDescription {\n name: string;\n def: string;\n}\n\ninterface ReferenceDescription {\n name: string;\n table: string;\n condef: string;\n}\n\ninterface CheckConstraintDescription {\n name: string;\n def: string;\n}\n\ninterface TableDescription {\n columns: ColumnDescription[];\n indexes: IndexDescription[];\n foreignKeyConstraints: ForeignKeyConstraintDescription[];\n references: ReferenceDescription[];\n checkConstraints: CheckConstraintDescription[];\n}\n\nexport interface DatabaseDescription {\n tables: Record<string, TableDescription>;\n enums: Record<string, string[]>;\n}\n\ninterface DescribeOptions {\n ignoreTables?: string[];\n ignoreColumns?: string[];\n ignoreEnums?: string[];\n}\n\nasync function describeWithPool(\n pool: PostgresPool,\n options: DescribeOptions,\n): Promise<DatabaseDescription> {\n const ignoreTables = options?.ignoreTables || [];\n const ignoreEnums = options?.ignoreEnums || [];\n let ignoreColumns: Record<string, string[]> = {};\n\n const output: DatabaseDescription = {\n tables: {},\n enums: {},\n };\n\n // Get the names of the tables and filter out any ignored tables\n const tablesRes = await pool.queryAsync(sql.get_tables, []);\n const tables = tablesRes.rows.filter((table) => ignoreTables.indexOf(table.name) === -1);\n\n // Transform ignored columns into a map from table names to arrays\n // of column names\n if (options.ignoreColumns && Array.isArray(options.ignoreColumns)) {\n ignoreColumns = options.ignoreColumns\n .filter((ignore) => {\n return /^[^\\s.]*\\.[^\\s.]*$/.test(ignore);\n })\n .reduce(\n (result, value) => {\n const res = /^(([^\\s.]*)\\.([^\\s.]*))$/.exec(value);\n if (!res) {\n throw new Error(`Invalid ignore column: ${value}`);\n }\n const table = res[2];\n const column = res[3];\n (result[table] || (result[table] = [])).push(column);\n return result;\n },\n {} as Record<string, string[]>,\n );\n }\n\n // Get column info for each table\n for (const table of tables) {\n const columnResults = await pool.queryAsync(sql.get_columns_for_table, {\n oid: table.oid,\n });\n\n const columns = columnResults.rows.filter((row) => {\n return (ignoreColumns[table.name] || []).indexOf(row.name) === -1;\n });\n\n const indexResults = await pool.queryAsync(sql.get_indexes_for_table, {\n oid: table.oid,\n });\n\n const foreignKeyConstraintResults = await pool.queryAsync(\n sql.get_foreign_key_constraints_for_table,\n {\n oid: table.oid,\n },\n );\n\n const referenceResults = await pool.queryAsync(sql.get_references_for_table, {\n oid: table.oid,\n });\n\n // Filter out references from ignored tables\n const references = referenceResults.rows.filter((row) => {\n return ignoreTables.indexOf(row.table) === -1;\n });\n\n const checkConstraintResults = await pool.queryAsync(sql.get_check_constraints_for_table, {\n oid: table.oid,\n });\n\n output.tables[table.name] = {\n columns,\n indexes: indexResults.rows,\n foreignKeyConstraints: foreignKeyConstraintResults.rows,\n references,\n checkConstraints: checkConstraintResults.rows,\n };\n }\n\n // Get all enums\n const enumsRes = await pool.queryAsync(sql.get_enums, []);\n\n // Filter ignored enums\n const rows = enumsRes.rows.filter((row) => {\n return ignoreEnums.indexOf(row.name) === -1;\n });\n\n rows.forEach((row) => {\n output.enums[row.name] = parsePostgresArray(row.values);\n });\n\n return output;\n}\n\n/**\n * Will produce a description of a given database's schema. This will include\n * information about tables, enums, constraints, indices, etc.\n */\nexport async function describeDatabase(\n databaseName: string,\n options: DescribeOptions = {},\n): Promise<DatabaseDescription> {\n // Connect to the database.\n const pool = new PostgresPool();\n const pgConfig = {\n user: 'postgres',\n database: databaseName,\n host: 'localhost',\n max: 10,\n idleTimeoutMillis: 30000,\n };\n function idleErrorHandler(err: Error) {\n throw err;\n }\n await pool.initAsync(pgConfig, idleErrorHandler);\n\n try {\n return await describeWithPool(pool, options);\n } finally {\n await pool.closeAsync();\n }\n}\n\nexport function formatDatabaseDescription(\n description: DatabaseDescription,\n options = { coloredOutput: true },\n): { tables: Record<string, string>; enums: Record<string, string> } {\n const output = {\n tables: {} as Record<string, string>,\n enums: {} as Record<string, string>,\n };\n\n Object.keys(description.tables).forEach((tableName) => (output.tables[tableName] = ''));\n\n /**\n * Optionally applies the given formatter to the text if colored output is\n * enabled.\n */\n function formatText(text: string, formatter: (s: string) => string): string {\n if (options.coloredOutput) {\n return formatter(text);\n }\n return text;\n }\n\n Object.entries(description.tables).forEach(([tableName, table]) => {\n if (table.columns.length > 0) {\n output.tables[tableName] += formatText('columns\\n', chalk.underline);\n output.tables[tableName] += table.columns\n .map((row) => {\n let rowText = formatText(` ${row.name}`, chalk.bold);\n rowText += ':' + formatText(` ${row.type}`, chalk.green);\n if (row.notnull) {\n rowText += formatText(' not null', chalk.gray);\n }\n if (row.default) {\n rowText += formatText(` default ${row.default}`, chalk.gray);\n }\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.indexes.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('indexes\\n', chalk.underline);\n output.tables[tableName] += table.indexes\n .map((row) => {\n const using = row.indexdef.substring(row.indexdef.indexOf('USING '));\n let rowText = formatText(` ${row.name}`, chalk.bold) + ':';\n // Primary indexes are implicitly unique, so we don't need to\n // capture that explicitly.\n if (row.isunique && !row.isprimary) {\n if (!row.constraintdef || row.constraintdef.indexOf('UNIQUE') === -1) {\n // Some unique indexes don't include the UNIQUE constraint\n // as part of the constraint definition, so we need to capture\n // that manually.\n rowText += formatText(' UNIQUE', chalk.green);\n }\n }\n rowText += row.constraintdef ? formatText(` ${row.constraintdef}`, chalk.green) : '';\n rowText += using ? formatText(` ${using}`, chalk.green) : '';\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.checkConstraints.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('check constraints\\n', chalk.underline);\n output.tables[tableName] += table.checkConstraints\n .map((row) => {\n // Particularly long constraints are formatted as multiple lines.\n // We'll collapse them into a single line for better appearance in\n // the resulting file.\n //\n // The first replace handles lines that end with a parenthesis: we\n // want to avoid spaces between the parenthesis and the next token.\n //\n // The second replace handles all other lines: we want to collapse\n // all leading whitespace into a single space.\n const def = row.def.replace(/\\(\\n/g, '(').replace(/\\n\\s*/g, ' ');\n\n let rowText = formatText(` ${row.name}:`, chalk.bold);\n rowText += formatText(` ${def}`, chalk.green);\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.foreignKeyConstraints.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('foreign-key constraints\\n', chalk.underline);\n output.tables[tableName] += table.foreignKeyConstraints\n .map((row) => {\n let rowText = formatText(` ${row.name}:`, chalk.bold);\n rowText += formatText(` ${row.def}`, chalk.green);\n return rowText;\n })\n .join('\\n');\n }\n\n if (table.references.length > 0) {\n if (output.tables[tableName].length !== 0) {\n output.tables[tableName] += '\\n\\n';\n }\n output.tables[tableName] += formatText('referenced by\\n', chalk.underline);\n output.tables[tableName] += table.references\n ?.map((row) => {\n let rowText = formatText(` ${row.table}:`, chalk.bold);\n rowText += formatText(` ${row.condef}`, chalk.green);\n return rowText;\n })\n .join('\\n');\n }\n });\n\n Object.entries(description.enums).forEach(([enumName, enumValues]) => {\n output.enums[enumName] = formatText(enumValues.join(', '), chalk.gray);\n });\n\n // We need to tack on a newline to everything.\n Object.entries(output.tables).forEach(([tableName, table]) => {\n output.tables[tableName] = table + '\\n';\n });\n Object.entries(output.enums).forEach(([enumName, enumValues]) => {\n output.enums[enumName] = enumValues + '\\n';\n });\n\n return output;\n}\n"]}
|
package/dist/diff.js
CHANGED
|
@@ -17,8 +17,8 @@ async function diff(db1, db2, options) {
|
|
|
17
17
|
const description1 = await loadDescription(db1);
|
|
18
18
|
const description2 = await loadDescription(db2);
|
|
19
19
|
// Determine if both databases have the same tables
|
|
20
|
-
const tablesMissingFrom1 = _.difference(
|
|
21
|
-
const tablesMissingFrom2 = _.difference(
|
|
20
|
+
const tablesMissingFrom1 = _.difference(Object.keys(description2.tables), Object.keys(description1.tables));
|
|
21
|
+
const tablesMissingFrom2 = _.difference(Object.keys(description1.tables), Object.keys(description2.tables));
|
|
22
22
|
if (tablesMissingFrom1.length > 0) {
|
|
23
23
|
result += formatText(`Tables added to ${db2NameBold} (${db2.type})\n`, chalk.underline);
|
|
24
24
|
result += formatText(tablesMissingFrom1.map((table) => `+ ${table}`).join('\n') + '\n\n', chalk.green);
|
|
@@ -28,8 +28,8 @@ async function diff(db1, db2, options) {
|
|
|
28
28
|
result += formatText(tablesMissingFrom2.map((table) => `- ${table}`).join('\n') + '\n\n', chalk.red);
|
|
29
29
|
}
|
|
30
30
|
// Determine if both databases have the same enums
|
|
31
|
-
const enumsMissingFrom1 = _.difference(
|
|
32
|
-
const enumsMissingFrom2 = _.difference(
|
|
31
|
+
const enumsMissingFrom1 = _.difference(Object.keys(description2.enums), Object.keys(description1.enums));
|
|
32
|
+
const enumsMissingFrom2 = _.difference(Object.keys(description1.enums), Object.keys(description2.enums));
|
|
33
33
|
if (enumsMissingFrom1.length > 0) {
|
|
34
34
|
result += formatText(`Enums added to ${db2NameBold} (${db1.type})\n`, chalk.underline);
|
|
35
35
|
result += formatText(enumsMissingFrom1.map((enumName) => `+ ${enumName}`).join('\n') + '\n\n', chalk.green);
|
|
@@ -39,11 +39,11 @@ async function diff(db1, db2, options) {
|
|
|
39
39
|
result += formatText(enumsMissingFrom2.map((enumName) => `- ${enumName}`).join('\n') + '\n\n', chalk.red);
|
|
40
40
|
}
|
|
41
41
|
// Determine if the columns of any table differ
|
|
42
|
-
const intersection = _.intersection(
|
|
43
|
-
|
|
42
|
+
const intersection = _.intersection(Object.keys(description1.tables), Object.keys(description2.tables));
|
|
43
|
+
for (const table of intersection) {
|
|
44
44
|
const patch = structuredPatch(`tables/${table}`, `tables/${table}`, description1.tables[table], description2.tables[table]);
|
|
45
45
|
if (patch.hunks.length === 0)
|
|
46
|
-
|
|
46
|
+
continue;
|
|
47
47
|
const boldTable = formatText(table, chalk.bold);
|
|
48
48
|
result += formatText(`Differences in ${boldTable} table\n`, chalk.underline);
|
|
49
49
|
patch.hunks.forEach((hunk, index) => {
|
|
@@ -57,10 +57,10 @@ async function diff(db1, db2, options) {
|
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
59
|
result += '\n\n';
|
|
60
|
-
}
|
|
60
|
+
}
|
|
61
61
|
// Determine if the values of any enums differ
|
|
62
|
-
const enumsIntersection = _.intersection(
|
|
63
|
-
|
|
62
|
+
const enumsIntersection = _.intersection(Object.keys(description1.enums), Object.keys(description2.enums));
|
|
63
|
+
for (const enumName of enumsIntersection) {
|
|
64
64
|
// We don't need to do a particularly fancy diff here, since
|
|
65
65
|
// enums are just represented here as strings
|
|
66
66
|
if (description1.enums[enumName].trim() !== description2.enums[enumName].trim()) {
|
|
@@ -70,7 +70,7 @@ async function diff(db1, db2, options) {
|
|
|
70
70
|
result += formatText(`+ ${description2.enums[enumName].trim()}\n`, chalk.green);
|
|
71
71
|
result += '\n\n';
|
|
72
72
|
}
|
|
73
|
-
}
|
|
73
|
+
}
|
|
74
74
|
return result;
|
|
75
75
|
}
|
|
76
76
|
async function loadDescriptionFromDisk(dirPath) {
|
package/dist/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\n\nimport chalk from 'chalk';\nimport { structuredPatch } from 'diff';\nimport fs from 'fs-extra';\nimport _ from 'lodash';\n\nimport { describeDatabase, formatDatabaseDescription } from './describe.js';\n\ninterface DatabaseInfo {\n type: 'database';\n name: string;\n}\n\ninterface DirectoryInfo {\n type: 'directory';\n path: string;\n}\n\ntype DiffTarget = DatabaseInfo | DirectoryInfo;\n\ninterface DiffOptions {\n coloredOutput?: boolean;\n}\n\ninterface Description {\n tables: Record<string, string>;\n enums: Record<string, string>;\n}\n\nasync function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Promise<string> {\n function formatText(text: string, formatter?: ((s: string) => string) | null): string {\n if (options.coloredOutput && formatter) {\n return formatter(text);\n }\n return text;\n }\n\n const db2Name = db2.type === 'database' ? db2.name : db2.path;\n const db2NameBold = formatText(db2Name, chalk.bold);\n\n let result = '';\n\n const description1 = await loadDescription(db1);\n const description2 = await loadDescription(db2);\n\n // Determine if both databases have the same tables\n const tablesMissingFrom1 = _.difference(_.keys(description2.tables), _.keys(description1.tables));\n const tablesMissingFrom2 = _.difference(_.keys(description1.tables), _.keys(description2.tables));\n\n if (tablesMissingFrom1.length > 0) {\n result += formatText(`Tables added to ${db2NameBold} (${db2.type})\\n`, chalk.underline);\n result += formatText(\n tablesMissingFrom1.map((table) => `+ ${table}`).join('\\n') + '\\n\\n',\n chalk.green,\n );\n }\n\n if (tablesMissingFrom2.length > 0) {\n result += formatText(`Tables missing from ${db2NameBold} (${db2.type})\\n`, chalk.underline);\n result += formatText(\n tablesMissingFrom2.map((table) => `- ${table}`).join('\\n') + '\\n\\n',\n chalk.red,\n );\n }\n\n // Determine if both databases have the same enums\n const enumsMissingFrom1 = _.difference(_.keys(description2.enums), _.keys(description1.enums));\n const enumsMissingFrom2 = _.difference(_.keys(description1.enums), _.keys(description2.enums));\n\n if (enumsMissingFrom1.length > 0) {\n result += formatText(`Enums added to ${db2NameBold} (${db1.type})\\n`, chalk.underline);\n result += formatText(\n enumsMissingFrom1.map((enumName) => `+ ${enumName}`).join('\\n') + '\\n\\n',\n chalk.green,\n );\n }\n\n if (enumsMissingFrom2.length > 0) {\n result += formatText(`Enums missing from ${db2NameBold} (${db2.type})\\n`, chalk.underline);\n result += formatText(\n enumsMissingFrom2.map((enumName) => `- ${enumName}`).join('\\n') + '\\n\\n',\n chalk.red,\n );\n }\n\n // Determine if the columns of any table differ\n const intersection = _.intersection(_.keys(description1.tables), _.keys(description2.tables));\n _.forEach(intersection, (table) => {\n const patch = structuredPatch(\n `tables/${table}`,\n `tables/${table}`,\n description1.tables[table],\n description2.tables[table],\n );\n\n if (patch.hunks.length === 0) return;\n\n const boldTable = formatText(table, chalk.bold);\n result += formatText(`Differences in ${boldTable} table\\n`, chalk.underline);\n\n patch.hunks.forEach((hunk, index) => {\n if (index !== 0) {\n result += formatText('...\\n', chalk.gray);\n }\n hunk.lines.forEach((line) => {\n const color = line[0] === '+' ? chalk.green : line[0] === '-' ? chalk.red : null;\n result += formatText(line, color);\n result += '\\n';\n });\n });\n\n result += '\\n\\n';\n });\n\n // Determine if the values of any enums differ\n const enumsIntersection = _.intersection(_.keys(description1.enums), _.keys(description2.enums));\n _.forEach(enumsIntersection, (enumName) => {\n // We don't need to do a particularly fancy diff here, since\n // enums are just represented here as strings\n if (description1.enums[enumName].trim() !== description2.enums[enumName].trim()) {\n const boldEnum = formatText(enumName, chalk.bold);\n result += formatText(`Differences in ${boldEnum} enum\\n`);\n result += formatText(`- ${description1.enums[enumName].trim()}\\n`, chalk.red);\n result += formatText(`+ ${description2.enums[enumName].trim()}\\n`, chalk.green);\n result += '\\n\\n';\n }\n });\n\n return result;\n}\n\nasync function loadDescriptionFromDisk(dirPath: string): Promise<Description> {\n const description: Description = {\n tables: {},\n enums: {},\n };\n\n const tables = await fs.readdir(path.join(dirPath, 'tables'));\n for (const table of tables) {\n const data = await fs.readFile(path.join(dirPath, 'tables', table), 'utf8');\n description.tables[table.replace('.pg', '')] = data;\n }\n\n const enums = await fs.readdir(path.join(dirPath, 'enums'));\n for (const enumName of enums) {\n const data = await fs.readFile(path.join(dirPath, 'enums', enumName), 'utf8');\n description.enums[enumName.replace('.pg', '')] = data;\n }\n\n return description;\n}\n\nasync function loadDescriptionFromDatabase(name: string) {\n const description = await describeDatabase(name);\n return formatDatabaseDescription(description, { coloredOutput: false });\n}\n\nasync function loadDescription(db: DiffTarget): Promise<Description> {\n if (db.type === 'database') {\n return loadDescriptionFromDatabase(db.name);\n } else if (db.type === 'directory') {\n return loadDescriptionFromDisk(db.path);\n } else {\n throw new Error('Invalid database type');\n }\n}\n\nexport async function diffDatabases(database1: string, database2: string, options: DiffOptions) {\n return diff(\n {\n type: 'database',\n name: database1,\n },\n {\n type: 'database',\n name: database2,\n },\n options,\n );\n}\n\nexport async function diffDatabaseAndDirectory(\n database: string,\n directory: string,\n options: DiffOptions,\n) {\n return diff(\n {\n type: 'database',\n name: database,\n },\n {\n type: 'directory',\n path: directory,\n },\n options,\n );\n}\n\nexport async function diffDirectoryAndDatabase(\n directory: string,\n database: string,\n options: DiffOptions,\n) {\n return diff(\n {\n type: 'directory',\n path: directory,\n },\n {\n type: 'database',\n name: database,\n },\n options,\n );\n}\n\nexport async function diffDirectories(\n directory1: string,\n directory2: string,\n options: DiffOptions,\n) {\n return diff(\n {\n type: 'directory',\n path: directory1,\n },\n {\n type: 'directory',\n path: directory2,\n },\n options,\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\n\nimport chalk from 'chalk';\nimport { structuredPatch } from 'diff';\nimport fs from 'fs-extra';\nimport _ from 'lodash';\n\nimport { describeDatabase, formatDatabaseDescription } from './describe.js';\n\ninterface DatabaseInfo {\n type: 'database';\n name: string;\n}\n\ninterface DirectoryInfo {\n type: 'directory';\n path: string;\n}\n\ntype DiffTarget = DatabaseInfo | DirectoryInfo;\n\ninterface DiffOptions {\n coloredOutput?: boolean;\n}\n\ninterface Description {\n tables: Record<string, string>;\n enums: Record<string, string>;\n}\n\nasync function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Promise<string> {\n function formatText(text: string, formatter?: ((s: string) => string) | null): string {\n if (options.coloredOutput && formatter) {\n return formatter(text);\n }\n return text;\n }\n\n const db2Name = db2.type === 'database' ? db2.name : db2.path;\n const db2NameBold = formatText(db2Name, chalk.bold);\n\n let result = '';\n\n const description1 = await loadDescription(db1);\n const description2 = await loadDescription(db2);\n\n // Determine if both databases have the same tables\n const tablesMissingFrom1 = _.difference(\n Object.keys(description2.tables),\n Object.keys(description1.tables),\n );\n const tablesMissingFrom2 = _.difference(\n Object.keys(description1.tables),\n Object.keys(description2.tables),\n );\n\n if (tablesMissingFrom1.length > 0) {\n result += formatText(`Tables added to ${db2NameBold} (${db2.type})\\n`, chalk.underline);\n result += formatText(\n tablesMissingFrom1.map((table) => `+ ${table}`).join('\\n') + '\\n\\n',\n chalk.green,\n );\n }\n\n if (tablesMissingFrom2.length > 0) {\n result += formatText(`Tables missing from ${db2NameBold} (${db2.type})\\n`, chalk.underline);\n result += formatText(\n tablesMissingFrom2.map((table) => `- ${table}`).join('\\n') + '\\n\\n',\n chalk.red,\n );\n }\n\n // Determine if both databases have the same enums\n const enumsMissingFrom1 = _.difference(\n Object.keys(description2.enums),\n Object.keys(description1.enums),\n );\n const enumsMissingFrom2 = _.difference(\n Object.keys(description1.enums),\n Object.keys(description2.enums),\n );\n\n if (enumsMissingFrom1.length > 0) {\n result += formatText(`Enums added to ${db2NameBold} (${db1.type})\\n`, chalk.underline);\n result += formatText(\n enumsMissingFrom1.map((enumName) => `+ ${enumName}`).join('\\n') + '\\n\\n',\n chalk.green,\n );\n }\n\n if (enumsMissingFrom2.length > 0) {\n result += formatText(`Enums missing from ${db2NameBold} (${db2.type})\\n`, chalk.underline);\n result += formatText(\n enumsMissingFrom2.map((enumName) => `- ${enumName}`).join('\\n') + '\\n\\n',\n chalk.red,\n );\n }\n\n // Determine if the columns of any table differ\n const intersection = _.intersection(\n Object.keys(description1.tables),\n Object.keys(description2.tables),\n );\n for (const table of intersection) {\n const patch = structuredPatch(\n `tables/${table}`,\n `tables/${table}`,\n description1.tables[table],\n description2.tables[table],\n );\n\n if (patch.hunks.length === 0) continue;\n\n const boldTable = formatText(table, chalk.bold);\n result += formatText(`Differences in ${boldTable} table\\n`, chalk.underline);\n\n patch.hunks.forEach((hunk, index) => {\n if (index !== 0) {\n result += formatText('...\\n', chalk.gray);\n }\n hunk.lines.forEach((line) => {\n const color = line[0] === '+' ? chalk.green : line[0] === '-' ? chalk.red : null;\n result += formatText(line, color);\n result += '\\n';\n });\n });\n\n result += '\\n\\n';\n }\n\n // Determine if the values of any enums differ\n const enumsIntersection = _.intersection(\n Object.keys(description1.enums),\n Object.keys(description2.enums),\n );\n for (const enumName of enumsIntersection) {\n // We don't need to do a particularly fancy diff here, since\n // enums are just represented here as strings\n if (description1.enums[enumName].trim() !== description2.enums[enumName].trim()) {\n const boldEnum = formatText(enumName, chalk.bold);\n result += formatText(`Differences in ${boldEnum} enum\\n`);\n result += formatText(`- ${description1.enums[enumName].trim()}\\n`, chalk.red);\n result += formatText(`+ ${description2.enums[enumName].trim()}\\n`, chalk.green);\n result += '\\n\\n';\n }\n }\n\n return result;\n}\n\nasync function loadDescriptionFromDisk(dirPath: string): Promise<Description> {\n const description: Description = {\n tables: {},\n enums: {},\n };\n\n const tables = await fs.readdir(path.join(dirPath, 'tables'));\n for (const table of tables) {\n const data = await fs.readFile(path.join(dirPath, 'tables', table), 'utf8');\n description.tables[table.replace('.pg', '')] = data;\n }\n\n const enums = await fs.readdir(path.join(dirPath, 'enums'));\n for (const enumName of enums) {\n const data = await fs.readFile(path.join(dirPath, 'enums', enumName), 'utf8');\n description.enums[enumName.replace('.pg', '')] = data;\n }\n\n return description;\n}\n\nasync function loadDescriptionFromDatabase(name: string) {\n const description = await describeDatabase(name);\n return formatDatabaseDescription(description, { coloredOutput: false });\n}\n\nasync function loadDescription(db: DiffTarget): Promise<Description> {\n if (db.type === 'database') {\n return loadDescriptionFromDatabase(db.name);\n } else if (db.type === 'directory') {\n return loadDescriptionFromDisk(db.path);\n } else {\n throw new Error('Invalid database type');\n }\n}\n\nexport async function diffDatabases(database1: string, database2: string, options: DiffOptions) {\n return diff(\n {\n type: 'database',\n name: database1,\n },\n {\n type: 'database',\n name: database2,\n },\n options,\n );\n}\n\nexport async function diffDatabaseAndDirectory(\n database: string,\n directory: string,\n options: DiffOptions,\n) {\n return diff(\n {\n type: 'database',\n name: database,\n },\n {\n type: 'directory',\n path: directory,\n },\n options,\n );\n}\n\nexport async function diffDirectoryAndDatabase(\n directory: string,\n database: string,\n options: DiffOptions,\n) {\n return diff(\n {\n type: 'directory',\n path: directory,\n },\n {\n type: 'database',\n name: database,\n },\n options,\n );\n}\n\nexport async function diffDirectories(\n directory1: string,\n directory2: string,\n options: DiffOptions,\n) {\n return diff(\n {\n type: 'directory',\n path: directory1,\n },\n {\n type: 'directory',\n path: directory2,\n },\n options,\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/postgres-tools",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.14",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"pg-describe": "./dist/bin/pg-describe.js",
|
|
8
|
-
"pg-diff": "./dist/bin/pg-diff.js"
|
|
9
|
-
},
|
|
10
5
|
"repository": {
|
|
11
6
|
"type": "git",
|
|
12
7
|
"url": "https://github.com/PrairieLearn/PrairieLearn.git",
|
|
13
8
|
"directory": "packages/postgres-tools"
|
|
14
9
|
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"pg-describe": "./dist/bin/pg-describe.js",
|
|
12
|
+
"pg-diff": "./dist/bin/pg-diff.js"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc && tscp",
|
|
17
17
|
"dev": "tsc --watch --preserveWatchOutput & tscp --watch"
|
|
18
18
|
},
|
|
19
|
-
"devDependencies": {
|
|
20
|
-
"@prairielearn/tsconfig": "^0.0.0",
|
|
21
|
-
"@types/diff": "^7.0.1",
|
|
22
|
-
"@types/fs-extra": "^11.0.4",
|
|
23
|
-
"@types/lodash": "^4.17.16",
|
|
24
|
-
"@types/node": "^20.17.23",
|
|
25
|
-
"@types/yargs": "^17.0.33",
|
|
26
|
-
"typescript": "^5.8.2",
|
|
27
|
-
"typescript-cp": "^0.1.9"
|
|
28
|
-
},
|
|
29
19
|
"dependencies": {
|
|
30
|
-
"@prairielearn/postgres": "^2.1.
|
|
20
|
+
"@prairielearn/postgres": "^2.1.10",
|
|
31
21
|
"async": "^3.2.6",
|
|
32
22
|
"chalk": "^5.4.1",
|
|
33
23
|
"diff": "^7.0.0",
|
|
34
24
|
"fs-extra": "^11.3.0",
|
|
35
25
|
"lodash": "^4.17.21",
|
|
36
|
-
"postgres-array": "^3.0.
|
|
26
|
+
"postgres-array": "^3.0.4",
|
|
37
27
|
"yargs": "^17.7.2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@prairielearn/tsconfig": "^0.0.0",
|
|
31
|
+
"@types/diff": "^7.0.2",
|
|
32
|
+
"@types/fs-extra": "^11.0.4",
|
|
33
|
+
"@types/lodash": "^4.17.16",
|
|
34
|
+
"@types/node": "^20.17.30",
|
|
35
|
+
"@types/yargs": "^17.0.33",
|
|
36
|
+
"typescript": "^5.8.2",
|
|
37
|
+
"typescript-cp": "^0.1.9"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/src/bin/pg-describe.ts
CHANGED
|
@@ -5,13 +5,12 @@ import path from 'path';
|
|
|
5
5
|
import async from 'async';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import fs from 'fs-extra';
|
|
8
|
-
import _ from 'lodash';
|
|
9
8
|
import yargs from 'yargs';
|
|
10
9
|
|
|
11
10
|
import {
|
|
11
|
+
type DatabaseDescription,
|
|
12
12
|
describeDatabase,
|
|
13
13
|
formatDatabaseDescription,
|
|
14
|
-
type DatabaseDescription,
|
|
15
14
|
} from '../describe.js';
|
|
16
15
|
|
|
17
16
|
const args = yargs(process.argv.slice(2))
|
|
@@ -84,17 +83,17 @@ describeDatabase(argv._[0].toString(), options).then(
|
|
|
84
83
|
|
|
85
84
|
function printDescription(description: DatabaseDescription) {
|
|
86
85
|
const formattedDescription = formatDatabaseDescription(description, { coloredOutput });
|
|
87
|
-
|
|
86
|
+
for (const tableName of Object.keys(formattedDescription.tables).sort()) {
|
|
88
87
|
process.stdout.write(formatText(`[table] ${tableName}\n`, chalk.bold));
|
|
89
88
|
process.stdout.write(formattedDescription.tables[tableName]);
|
|
90
89
|
process.stdout.write('\n\n');
|
|
91
|
-
}
|
|
90
|
+
}
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
for (const enumName of Object.keys(formattedDescription.enums).sort()) {
|
|
94
93
|
process.stdout.write(formatText(`[enum] ${enumName}\n`, chalk.bold));
|
|
95
94
|
process.stdout.write(formattedDescription.enums[enumName]);
|
|
96
95
|
process.stdout.write('\n\n');
|
|
97
|
-
}
|
|
96
|
+
}
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
async function writeDescriptionToDisk(description: DatabaseDescription, dir: string) {
|
package/src/bin/pg-diff.ts
CHANGED
package/src/describe.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { parse as parsePostgresArray } from 'postgres-array';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { PostgresPool, loadSqlEquiv } from '@prairielearn/postgres';
|
|
5
5
|
|
|
6
6
|
const sql = loadSqlEquiv(import.meta.url);
|
|
7
7
|
|
package/src/diff.ts
CHANGED
|
@@ -45,8 +45,14 @@ async function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Pro
|
|
|
45
45
|
const description2 = await loadDescription(db2);
|
|
46
46
|
|
|
47
47
|
// Determine if both databases have the same tables
|
|
48
|
-
const tablesMissingFrom1 = _.difference(
|
|
49
|
-
|
|
48
|
+
const tablesMissingFrom1 = _.difference(
|
|
49
|
+
Object.keys(description2.tables),
|
|
50
|
+
Object.keys(description1.tables),
|
|
51
|
+
);
|
|
52
|
+
const tablesMissingFrom2 = _.difference(
|
|
53
|
+
Object.keys(description1.tables),
|
|
54
|
+
Object.keys(description2.tables),
|
|
55
|
+
);
|
|
50
56
|
|
|
51
57
|
if (tablesMissingFrom1.length > 0) {
|
|
52
58
|
result += formatText(`Tables added to ${db2NameBold} (${db2.type})\n`, chalk.underline);
|
|
@@ -65,8 +71,14 @@ async function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Pro
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
// Determine if both databases have the same enums
|
|
68
|
-
const enumsMissingFrom1 = _.difference(
|
|
69
|
-
|
|
74
|
+
const enumsMissingFrom1 = _.difference(
|
|
75
|
+
Object.keys(description2.enums),
|
|
76
|
+
Object.keys(description1.enums),
|
|
77
|
+
);
|
|
78
|
+
const enumsMissingFrom2 = _.difference(
|
|
79
|
+
Object.keys(description1.enums),
|
|
80
|
+
Object.keys(description2.enums),
|
|
81
|
+
);
|
|
70
82
|
|
|
71
83
|
if (enumsMissingFrom1.length > 0) {
|
|
72
84
|
result += formatText(`Enums added to ${db2NameBold} (${db1.type})\n`, chalk.underline);
|
|
@@ -85,8 +97,11 @@ async function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Pro
|
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
// Determine if the columns of any table differ
|
|
88
|
-
const intersection = _.intersection(
|
|
89
|
-
|
|
100
|
+
const intersection = _.intersection(
|
|
101
|
+
Object.keys(description1.tables),
|
|
102
|
+
Object.keys(description2.tables),
|
|
103
|
+
);
|
|
104
|
+
for (const table of intersection) {
|
|
90
105
|
const patch = structuredPatch(
|
|
91
106
|
`tables/${table}`,
|
|
92
107
|
`tables/${table}`,
|
|
@@ -94,7 +109,7 @@ async function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Pro
|
|
|
94
109
|
description2.tables[table],
|
|
95
110
|
);
|
|
96
111
|
|
|
97
|
-
if (patch.hunks.length === 0)
|
|
112
|
+
if (patch.hunks.length === 0) continue;
|
|
98
113
|
|
|
99
114
|
const boldTable = formatText(table, chalk.bold);
|
|
100
115
|
result += formatText(`Differences in ${boldTable} table\n`, chalk.underline);
|
|
@@ -111,11 +126,14 @@ async function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Pro
|
|
|
111
126
|
});
|
|
112
127
|
|
|
113
128
|
result += '\n\n';
|
|
114
|
-
}
|
|
129
|
+
}
|
|
115
130
|
|
|
116
131
|
// Determine if the values of any enums differ
|
|
117
|
-
const enumsIntersection = _.intersection(
|
|
118
|
-
|
|
132
|
+
const enumsIntersection = _.intersection(
|
|
133
|
+
Object.keys(description1.enums),
|
|
134
|
+
Object.keys(description2.enums),
|
|
135
|
+
);
|
|
136
|
+
for (const enumName of enumsIntersection) {
|
|
119
137
|
// We don't need to do a particularly fancy diff here, since
|
|
120
138
|
// enums are just represented here as strings
|
|
121
139
|
if (description1.enums[enumName].trim() !== description2.enums[enumName].trim()) {
|
|
@@ -125,7 +143,7 @@ async function diff(db1: DiffTarget, db2: DiffTarget, options: DiffOptions): Pro
|
|
|
125
143
|
result += formatText(`+ ${description2.enums[enumName].trim()}\n`, chalk.green);
|
|
126
144
|
result += '\n\n';
|
|
127
145
|
}
|
|
128
|
-
}
|
|
146
|
+
}
|
|
129
147
|
|
|
130
148
|
return result;
|
|
131
149
|
}
|