rake-db 2.3.9 → 2.3.12

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # rake-db
2
2
 
3
+ ## 2.3.12
4
+
5
+ ### Patch Changes
6
+
7
+ - Handle table ordering by foreign key when pulling db
8
+ - Updated dependencies
9
+ - pqb@0.9.7
10
+
11
+ ## 2.3.11
12
+
13
+ ### Patch Changes
14
+
15
+ - Add generating extension to db pull
16
+
17
+ ## 2.3.10
18
+
19
+ ### Patch Changes
20
+
21
+ - Add custom commands to rake-db
22
+
3
23
  ## 2.3.9
4
24
 
5
25
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -311,7 +311,7 @@ declare class MigrationBase {
311
311
  }
312
312
  declare const runCodeUpdater: (migration: MigrationBase, ast: RakeDbAst) => Promise<void> | undefined;
313
313
 
314
- declare type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameTable | RakeDbAst.Schema | RakeDbAst.Extension;
314
+ declare type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameTable | RakeDbAst.Schema | RakeDbAst.Extension | RakeDbAst.ForeignKey;
315
315
  declare namespace RakeDbAst {
316
316
  type Table = {
317
317
  type: 'table';
@@ -387,12 +387,19 @@ declare namespace RakeDbAst {
387
387
  ifExists?: boolean;
388
388
  ifNotExists?: boolean;
389
389
  };
390
+ type ForeignKey = {
391
+ type: 'foreignKey';
392
+ action: 'create';
393
+ tableSchema?: string;
394
+ tableName: string;
395
+ } & TableData.ForeignKey;
390
396
  }
391
397
 
392
398
  declare type Db = DbResult<typeof columnTypes>;
393
399
  declare type RakeDbConfig = {
394
400
  migrationsPath: string;
395
401
  migrationsTable: string;
402
+ commands: Record<string, (options: AdapterOptions[], config: RakeDbConfig, args: string[]) => Promise<void>>;
396
403
  requireTs(path: string): Promise<void>;
397
404
  noPrimaryKey?: NoPrimaryKeyOption;
398
405
  appCodeUpdater?: AppCodeUpdater;
package/dist/index.js CHANGED
@@ -50,8 +50,9 @@ var __spreadValues$6 = (a, b) => {
50
50
  };
51
51
  var __spreadProps$5 = (a, b) => __defProps$5(a, __getOwnPropDescs$5(b));
52
52
  const migrationConfigDefaults = {
53
- migrationsPath: path__default["default"].resolve("src", "migrations"),
53
+ migrationsPath: path__default["default"].resolve("src", "db", "migrations"),
54
54
  migrationsTable: "schemaMigrations",
55
+ commands: {},
55
56
  requireTs: (path2) => (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })(path2),
56
57
  log: true,
57
58
  logger: console,
@@ -1029,10 +1030,10 @@ class MigrationBase {
1029
1030
  return createSchema$1(this, !this.up, schemaName);
1030
1031
  }
1031
1032
  createExtension(name, options = {}) {
1032
- return createExtension(this, this.up, name, options);
1033
+ return createExtension$1(this, this.up, name, options);
1033
1034
  }
1034
1035
  dropExtension(name, options = {}) {
1035
- return createExtension(this, !this.up, name, options);
1036
+ return createExtension$1(this, !this.up, name, options);
1036
1037
  }
1037
1038
  async tableExists(tableName) {
1038
1039
  return queryExists(this, {
@@ -1094,7 +1095,7 @@ const createSchema$1 = async (migration, up, name) => {
1094
1095
  );
1095
1096
  await runCodeUpdater(migration, ast);
1096
1097
  };
1097
- const createExtension = async (migration, up, name, options) => {
1098
+ const createExtension$1 = async (migration, up, name, options) => {
1098
1099
  const ast = __spreadValues$2({
1099
1100
  type: "extension",
1100
1101
  action: up ? "create" : "drop",
@@ -1717,24 +1718,8 @@ const fkeyActionMap = {
1717
1718
  };
1718
1719
  const structureToAst = async (db) => {
1719
1720
  const ast = [];
1720
- const [
1721
- schemas,
1722
- tables,
1723
- allColumns,
1724
- allPrimaryKeys,
1725
- allIndexes,
1726
- allForeignKeys,
1727
- extensions
1728
- ] = await Promise.all([
1729
- db.getSchemas(),
1730
- db.getTables(),
1731
- db.getColumns(),
1732
- db.getPrimaryKeys(),
1733
- db.getIndexes(),
1734
- db.getForeignKeys(),
1735
- db.getExtensions()
1736
- ]);
1737
- for (const name of schemas) {
1721
+ const data = await getData(db);
1722
+ for (const name of data.schemas) {
1738
1723
  if (name === "public")
1739
1724
  continue;
1740
1725
  ast.push({
@@ -1743,108 +1728,51 @@ const structureToAst = async (db) => {
1743
1728
  name
1744
1729
  });
1745
1730
  }
1746
- for (const table of tables) {
1747
- const { schemaName, name } = table;
1748
- if (name === "schemaMigrations")
1749
- continue;
1750
- const belongsToTable = makeBelongsToTable(schemaName, name);
1751
- const columns = allColumns.filter(belongsToTable);
1752
- const primaryKey = allPrimaryKeys.find(belongsToTable);
1753
- const tableIndexes = allIndexes.filter(belongsToTable);
1754
- const tableForeignKeys = allForeignKeys.filter(belongsToTable);
1755
- const shape = {};
1756
- for (let item of columns) {
1757
- const isSerial = getIsSerial(item);
1758
- if (isSerial) {
1759
- item = __spreadProps(__spreadValues({}, item), { default: void 0 });
1760
- }
1761
- const klass = pqb.columnsByType[getColumnType(item, isSerial)];
1762
- if (!klass) {
1763
- throw new Error(`Column type \`${item.type}\` is not supported`);
1731
+ const pendingTables = {};
1732
+ for (const table of data.tables) {
1733
+ const key = `${table.schemaName}.${table.name}`;
1734
+ const dependsOn = /* @__PURE__ */ new Set();
1735
+ for (const fk of data.foreignKeys) {
1736
+ if (fk.schemaName !== table.schemaName || fk.tableName !== table.name)
1737
+ continue;
1738
+ const otherKey = `${fk.foreignTableSchemaName}.${fk.foreignTableName}`;
1739
+ if (otherKey !== key) {
1740
+ dependsOn.add(otherKey);
1764
1741
  }
1765
- let column = pqb.instantiateColumn(klass, item);
1766
- if ((primaryKey == null ? void 0 : primaryKey.columnNames.length) === 1 && (primaryKey == null ? void 0 : primaryKey.columnNames[0]) === item.name) {
1767
- column = column.primaryKey();
1768
- }
1769
- const indexes = tableIndexes.filter(
1770
- (it) => it.columns.length === 1 && "column" in it.columns[0] && it.columns[0].column === item.name
1771
- );
1772
- for (const index of indexes) {
1773
- const options = index.columns[0];
1774
- column = column.index({
1775
- collate: options.collate,
1776
- opclass: options.opclass,
1777
- order: options.order,
1778
- name: index.name !== getIndexName(name, index.columns) ? index.name : void 0,
1779
- using: index.using === "btree" ? void 0 : index.using,
1780
- unique: index.isUnique,
1781
- include: index.include,
1782
- with: index.with,
1783
- tablespace: index.tablespace,
1784
- where: index.where
1785
- });
1786
- }
1787
- const foreignKeys = tableForeignKeys.filter(
1788
- (it) => it.columnNames.length === 1 && it.columnNames[0] === item.name
1789
- );
1790
- for (const foreignKey of foreignKeys) {
1791
- column = column.foreignKey(
1792
- foreignKey.foreignTableName,
1793
- foreignKey.foreignColumnNames[0],
1794
- {
1795
- name: foreignKey.name && foreignKey.name !== getForeignKeyName(name, foreignKey.columnNames) ? foreignKey.name : void 0,
1796
- match: matchMap[foreignKey.match],
1797
- onUpdate: fkeyActionMap[foreignKey.onUpdate],
1798
- onDelete: fkeyActionMap[foreignKey.onDelete]
1799
- }
1800
- );
1742
+ }
1743
+ pendingTables[key] = { table, dependsOn };
1744
+ }
1745
+ for (const key in pendingTables) {
1746
+ const { table, dependsOn } = pendingTables[key];
1747
+ if (!dependsOn.size) {
1748
+ pushTableAst(ast, data, table, pendingTables);
1749
+ }
1750
+ }
1751
+ const outerFKeys = [];
1752
+ for (const key in pendingTables) {
1753
+ const innerFKeys = [];
1754
+ const { table } = pendingTables[key];
1755
+ for (const fkey of data.foreignKeys) {
1756
+ if (fkey.schemaName !== table.schemaName || fkey.tableName !== table.name)
1757
+ continue;
1758
+ const otherKey = `${fkey.foreignTableSchemaName}.${fkey.foreignTableName}`;
1759
+ if (!pendingTables[otherKey] || otherKey === key) {
1760
+ innerFKeys.push(fkey);
1761
+ } else {
1762
+ outerFKeys.push([fkey, table]);
1801
1763
  }
1802
- shape[item.name] = column;
1803
1764
  }
1804
- ast.push({
1805
- type: "table",
1765
+ pushTableAst(ast, data, table, pendingTables, innerFKeys);
1766
+ }
1767
+ for (const [fkey, table] of outerFKeys) {
1768
+ ast.push(__spreadProps(__spreadValues({}, foreignKeyToAst(fkey)), {
1769
+ type: "foreignKey",
1806
1770
  action: "create",
1807
- schema: schemaName === "public" ? void 0 : schemaName,
1808
- comment: table.comment,
1809
- name,
1810
- shape,
1811
- noPrimaryKey: primaryKey ? "error" : "ignore",
1812
- primaryKey: primaryKey && primaryKey.columnNames.length > 1 ? {
1813
- columns: primaryKey.columnNames,
1814
- options: primaryKey.name === `${name}_pkey` ? void 0 : { name: primaryKey.name }
1815
- } : void 0,
1816
- indexes: tableIndexes.filter(
1817
- (index) => index.columns.length > 1 || index.columns.some((it) => "expression" in it)
1818
- ).map((index) => ({
1819
- columns: index.columns.map((it) => __spreadProps(__spreadValues({}, "column" in it ? { column: it.column } : { expression: it.expression }), {
1820
- collate: it.collate,
1821
- opclass: it.opclass,
1822
- order: it.order
1823
- })),
1824
- options: {
1825
- name: index.name !== getIndexName(name, index.columns) ? index.name : void 0,
1826
- using: index.using === "btree" ? void 0 : index.using,
1827
- unique: index.isUnique,
1828
- include: index.include,
1829
- with: index.with,
1830
- tablespace: index.tablespace,
1831
- where: index.where
1832
- }
1833
- })),
1834
- foreignKeys: tableForeignKeys.filter((it) => it.columnNames.length > 1).map((it) => ({
1835
- columns: it.columnNames,
1836
- fnOrTable: it.foreignTableName,
1837
- foreignColumns: it.foreignColumnNames,
1838
- options: {
1839
- name: it.name && it.name !== getForeignKeyName(name, it.columnNames) ? it.name : void 0,
1840
- match: matchMap[it.match],
1841
- onUpdate: fkeyActionMap[it.onUpdate],
1842
- onDelete: fkeyActionMap[it.onDelete]
1843
- }
1844
- }))
1845
- });
1771
+ tableSchema: table.schemaName === "public" ? void 0 : table.schemaName,
1772
+ tableName: fkey.tableName
1773
+ }));
1846
1774
  }
1847
- for (const it of extensions) {
1775
+ for (const it of data.extensions) {
1848
1776
  ast.push({
1849
1777
  type: "extension",
1850
1778
  action: "create",
@@ -1855,6 +1783,34 @@ const structureToAst = async (db) => {
1855
1783
  }
1856
1784
  return ast;
1857
1785
  };
1786
+ const getData = async (db) => {
1787
+ const [
1788
+ schemas,
1789
+ tables,
1790
+ columns,
1791
+ primaryKeys,
1792
+ indexes,
1793
+ foreignKeys,
1794
+ extensions
1795
+ ] = await Promise.all([
1796
+ db.getSchemas(),
1797
+ db.getTables(),
1798
+ db.getColumns(),
1799
+ db.getPrimaryKeys(),
1800
+ db.getIndexes(),
1801
+ db.getForeignKeys(),
1802
+ db.getExtensions()
1803
+ ]);
1804
+ return {
1805
+ schemas,
1806
+ tables,
1807
+ columns,
1808
+ primaryKeys,
1809
+ indexes,
1810
+ foreignKeys,
1811
+ extensions
1812
+ };
1813
+ };
1858
1814
  const makeBelongsToTable = (schema, table) => (item) => item.schemaName === schema && item.tableName === table;
1859
1815
  const getIsSerial = (item) => {
1860
1816
  if (item.type === "int2" || item.type === "int4" || item.type === "int8") {
@@ -1872,16 +1828,134 @@ const getColumnType = (item, isSerial) => {
1872
1828
  }
1873
1829
  return item.type;
1874
1830
  };
1831
+ const pushTableAst = (ast, data, table, pendingTables, innerFKeys = data.foreignKeys) => {
1832
+ const { schemaName, name } = table;
1833
+ const key = `${schemaName}.${table.name}`;
1834
+ delete pendingTables[key];
1835
+ if (name === "schemaMigrations")
1836
+ return;
1837
+ const belongsToTable = makeBelongsToTable(schemaName, name);
1838
+ const columns = data.columns.filter(belongsToTable);
1839
+ const primaryKey = data.primaryKeys.find(belongsToTable);
1840
+ const tableIndexes = data.indexes.filter(belongsToTable);
1841
+ const tableForeignKeys = innerFKeys.filter(belongsToTable);
1842
+ const shape = {};
1843
+ for (let item of columns) {
1844
+ const isSerial = getIsSerial(item);
1845
+ if (isSerial) {
1846
+ item = __spreadProps(__spreadValues({}, item), { default: void 0 });
1847
+ }
1848
+ const klass = pqb.columnsByType[getColumnType(item, isSerial)];
1849
+ if (!klass) {
1850
+ throw new Error(`Column type \`${item.type}\` is not supported`);
1851
+ }
1852
+ let column = pqb.instantiateColumn(klass, item);
1853
+ if ((primaryKey == null ? void 0 : primaryKey.columnNames.length) === 1 && (primaryKey == null ? void 0 : primaryKey.columnNames[0]) === item.name) {
1854
+ column = column.primaryKey();
1855
+ }
1856
+ const indexes = tableIndexes.filter(
1857
+ (it) => it.columns.length === 1 && "column" in it.columns[0] && it.columns[0].column === item.name
1858
+ );
1859
+ for (const index of indexes) {
1860
+ const options = index.columns[0];
1861
+ column = column.index({
1862
+ collate: options.collate,
1863
+ opclass: options.opclass,
1864
+ order: options.order,
1865
+ name: index.name !== getIndexName(name, index.columns) ? index.name : void 0,
1866
+ using: index.using === "btree" ? void 0 : index.using,
1867
+ unique: index.isUnique,
1868
+ include: index.include,
1869
+ with: index.with,
1870
+ tablespace: index.tablespace,
1871
+ where: index.where
1872
+ });
1873
+ }
1874
+ const foreignKeys = tableForeignKeys.filter(
1875
+ (it) => it.columnNames.length === 1 && it.columnNames[0] === item.name
1876
+ );
1877
+ for (const foreignKey of foreignKeys) {
1878
+ column = column.foreignKey(
1879
+ foreignKey.foreignTableName,
1880
+ foreignKey.foreignColumnNames[0],
1881
+ {
1882
+ name: foreignKey.name && foreignKey.name !== getForeignKeyName(name, foreignKey.columnNames) ? foreignKey.name : void 0,
1883
+ match: matchMap[foreignKey.match],
1884
+ onUpdate: fkeyActionMap[foreignKey.onUpdate],
1885
+ onDelete: fkeyActionMap[foreignKey.onDelete]
1886
+ }
1887
+ );
1888
+ }
1889
+ shape[item.name] = column;
1890
+ }
1891
+ ast.push({
1892
+ type: "table",
1893
+ action: "create",
1894
+ schema: schemaName === "public" ? void 0 : schemaName,
1895
+ comment: table.comment,
1896
+ name,
1897
+ shape,
1898
+ noPrimaryKey: primaryKey ? "error" : "ignore",
1899
+ primaryKey: primaryKey && primaryKey.columnNames.length > 1 ? {
1900
+ columns: primaryKey.columnNames,
1901
+ options: primaryKey.name === `${name}_pkey` ? void 0 : { name: primaryKey.name }
1902
+ } : void 0,
1903
+ indexes: tableIndexes.filter(
1904
+ (index) => index.columns.length > 1 || index.columns.some((it) => "expression" in it)
1905
+ ).map((index) => ({
1906
+ columns: index.columns.map((it) => __spreadProps(__spreadValues({}, "column" in it ? { column: it.column } : { expression: it.expression }), {
1907
+ collate: it.collate,
1908
+ opclass: it.opclass,
1909
+ order: it.order
1910
+ })),
1911
+ options: {
1912
+ name: index.name !== getIndexName(name, index.columns) ? index.name : void 0,
1913
+ using: index.using === "btree" ? void 0 : index.using,
1914
+ unique: index.isUnique,
1915
+ include: index.include,
1916
+ with: index.with,
1917
+ tablespace: index.tablespace,
1918
+ where: index.where
1919
+ }
1920
+ })),
1921
+ foreignKeys: tableForeignKeys.filter((it) => it.columnNames.length > 1).map(foreignKeyToAst)
1922
+ });
1923
+ for (const otherKey in pendingTables) {
1924
+ const item = pendingTables[otherKey];
1925
+ if (item.dependsOn.delete(key) && item.dependsOn.size === 0) {
1926
+ pushTableAst(ast, data, item.table, pendingTables);
1927
+ }
1928
+ }
1929
+ };
1930
+ const foreignKeyToAst = (fkey) => ({
1931
+ columns: fkey.columnNames,
1932
+ fnOrTable: fkey.foreignTableName,
1933
+ foreignColumns: fkey.foreignColumnNames,
1934
+ options: {
1935
+ name: fkey.name && fkey.name !== getForeignKeyName(fkey.tableName, fkey.columnNames) ? fkey.name : void 0,
1936
+ match: matchMap[fkey.match],
1937
+ onUpdate: fkeyActionMap[fkey.onUpdate],
1938
+ onDelete: fkeyActionMap[fkey.onDelete]
1939
+ }
1940
+ });
1875
1941
 
1876
1942
  const astToMigration = (ast) => {
1877
1943
  const code = [];
1878
1944
  for (const item of ast) {
1879
1945
  if (item.type === "schema" && item.action === "create") {
1880
1946
  code.push(createSchema(item));
1947
+ } else if (item.type === "extension" && item.action === "create") {
1948
+ if (code.length)
1949
+ code.push([]);
1950
+ code.push(...createExtension(item));
1881
1951
  } else if (item.type === "table" && item.action === "create") {
1882
1952
  if (code.length)
1883
1953
  code.push([]);
1884
1954
  code.push(...createTable(item));
1955
+ } else if (item.type === "foreignKey") {
1956
+ if (code.length)
1957
+ code.push([]);
1958
+ code.push(...createForeignKey(item));
1885
1959
  }
1886
1960
  }
1887
1961
  if (!code.length)
@@ -1896,6 +1970,21 @@ ${pqb.codeToString(code, " ", " ")}
1896
1970
  const createSchema = (ast) => {
1897
1971
  return `await db.createSchema(${pqb.singleQuote(ast.name)});`;
1898
1972
  };
1973
+ const createExtension = (ast) => {
1974
+ const code = [`await db.createExtension(${pqb.singleQuote(ast.name)}`];
1975
+ if (ast.schema || ast.version) {
1976
+ pqb.addCode(code, ", {");
1977
+ if (ast.schema) {
1978
+ code.push([`schema: ${pqb.singleQuote(ast.schema)},`]);
1979
+ }
1980
+ if (ast.version) {
1981
+ code.push([`version: ${pqb.singleQuote(ast.version)},`]);
1982
+ }
1983
+ pqb.addCode(code, "}");
1984
+ }
1985
+ pqb.addCode(code, ")");
1986
+ return code;
1987
+ };
1899
1988
  const createTable = (ast) => {
1900
1989
  const code = [];
1901
1990
  pqb.addCode(code, `await db.createTable(${quoteSchemaTable(ast)}, (t) => ({`);
@@ -1931,6 +2020,19 @@ const isTimestamp = (column) => {
1931
2020
  const { default: def } = column.data;
1932
2021
  return column instanceof pqb.TimestampColumn && !column.data.isNullable && def && typeof def === "object" && pqb.isRaw(def) && def.__raw === "now()";
1933
2022
  };
2023
+ const createForeignKey = (item) => {
2024
+ return [
2025
+ `await db.addForeignKey(`,
2026
+ [
2027
+ `${quoteSchemaTable({
2028
+ schema: item.tableSchema,
2029
+ name: item.tableName
2030
+ })},`,
2031
+ ...pqb.foreignKeyArgsToCode(item)
2032
+ ],
2033
+ ");"
2034
+ ];
2035
+ };
1934
2036
 
1935
2037
  const pullDbStructure = async (options, config) => {
1936
2038
  const adapter = new pqb.Adapter(options);
@@ -1961,6 +2063,8 @@ const rakeDb = async (options, partialConfig = {}, args = process.argv.slice(2))
1961
2063
  await generate(config, args.slice(1));
1962
2064
  } else if (command === "pull") {
1963
2065
  await pullDbStructure(pqb.toArray(options)[0], config);
2066
+ } else if (config.commands[command]) {
2067
+ await config.commands[command](pqb.toArray(options), config, args.slice(1));
1964
2068
  } else {
1965
2069
  printHelp();
1966
2070
  }