prostgles-server 2.0.185 → 2.0.188

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 (91) hide show
  1. package/dist/DboBuilder/insert.d.ts +5 -0
  2. package/dist/DboBuilder/insert.d.ts.map +1 -0
  3. package/dist/DboBuilder/insert.js +129 -0
  4. package/dist/DboBuilder/insert.js.map +1 -0
  5. package/dist/DboBuilder/insertDataParse.d.ts +11 -0
  6. package/dist/DboBuilder/insertDataParse.d.ts.map +1 -0
  7. package/dist/DboBuilder/insertDataParse.js +283 -0
  8. package/dist/DboBuilder/insertDataParse.js.map +1 -0
  9. package/dist/DboBuilder.d.ts +10 -5
  10. package/dist/DboBuilder.d.ts.map +1 -1
  11. package/dist/DboBuilder.js +121 -374
  12. package/dist/DboBuilder.js.map +1 -1
  13. package/dist/FileManager.d.ts.map +1 -1
  14. package/dist/FileManager.js +17 -12
  15. package/dist/FileManager.js.map +1 -1
  16. package/dist/Prostgles.d.ts +16 -14
  17. package/dist/Prostgles.d.ts.map +1 -1
  18. package/dist/Prostgles.js +7 -7
  19. package/dist/Prostgles.js.map +1 -1
  20. package/dist/PubSubManager.d.ts +1 -0
  21. package/dist/PubSubManager.d.ts.map +1 -1
  22. package/dist/PubSubManager.js +2 -1
  23. package/dist/PubSubManager.js.map +1 -1
  24. package/dist/QueryBuilder.d.ts +7 -3
  25. package/dist/QueryBuilder.d.ts.map +1 -1
  26. package/dist/QueryBuilder.js +7 -2
  27. package/dist/QueryBuilder.js.map +1 -1
  28. package/dist/TableConfig.d.ts +1 -4
  29. package/dist/TableConfig.d.ts.map +1 -1
  30. package/dist/TableConfig.js +16 -1
  31. package/dist/TableConfig.js.map +1 -1
  32. package/lib/DboBuilder/insert.d.ts +5 -0
  33. package/lib/DboBuilder/insert.d.ts.map +1 -0
  34. package/lib/DboBuilder/insert.js +128 -0
  35. package/lib/DboBuilder/insert.ts +138 -0
  36. package/lib/DboBuilder/insertDataParse.d.ts +11 -0
  37. package/lib/DboBuilder/insertDataParse.d.ts.map +1 -0
  38. package/lib/DboBuilder/insertDataParse.js +282 -0
  39. package/lib/DboBuilder/insertDataParse.ts +355 -0
  40. package/lib/DboBuilder.d.ts +10 -5
  41. package/lib/DboBuilder.d.ts.map +1 -1
  42. package/lib/DboBuilder.js +121 -374
  43. package/lib/DboBuilder.ts +138 -453
  44. package/lib/FileManager.d.ts.map +1 -1
  45. package/lib/FileManager.js +17 -12
  46. package/lib/FileManager.ts +18 -13
  47. package/lib/Prostgles.d.ts +16 -14
  48. package/lib/Prostgles.d.ts.map +1 -1
  49. package/lib/Prostgles.js +7 -7
  50. package/lib/Prostgles.ts +664 -650
  51. package/lib/PubSubManager.d.ts +1 -0
  52. package/lib/PubSubManager.d.ts.map +1 -1
  53. package/lib/PubSubManager.js +2 -1
  54. package/lib/PubSubManager.ts +3 -1
  55. package/lib/QueryBuilder.d.ts.map +1 -1
  56. package/lib/QueryBuilder.js +7 -2
  57. package/lib/QueryBuilder.ts +12 -7
  58. package/lib/SchemaWatchManager.ts +72 -0
  59. package/lib/TableConfig.d.ts +1 -4
  60. package/lib/TableConfig.d.ts.map +1 -1
  61. package/lib/TableConfig.js +16 -1
  62. package/lib/TableConfig.ts +21 -8
  63. package/package.json +3 -3
  64. package/tests/client/PID.txt +1 -1
  65. package/tests/client/package-lock.json +15 -15
  66. package/tests/client/package.json +1 -1
  67. package/tests/client/tsconfig.json +1 -1
  68. package/tests/client_only_queries.d.ts +1 -1
  69. package/tests/client_only_queries.d.ts.map +1 -1
  70. package/tests/client_only_queries.ts +1 -1
  71. package/tests/isomorphic_queries.d.ts +1 -1
  72. package/tests/isomorphic_queries.d.ts.map +1 -1
  73. package/tests/isomorphic_queries.js +49 -1
  74. package/tests/isomorphic_queries.ts +66 -4
  75. package/tests/manual_test/DBoGenerated.d.ts +398 -0
  76. package/tests/manual_test/index.d.ts +2 -0
  77. package/tests/manual_test/index.d.ts.map +1 -0
  78. package/tests/{config_test2 → manual_test}/index.html +14 -23
  79. package/tests/{config_test2 → manual_test}/index.js +21 -15
  80. package/tests/{config_test2 → manual_test}/index.ts +22 -17
  81. package/tests/{config_test2 → manual_test}/init.sql +36 -5
  82. package/tests/manual_test/package-lock.json +2483 -0
  83. package/tests/{config_test2 → manual_test}/package.json +6 -7
  84. package/tests/manual_test/tsconfig.json +21 -0
  85. package/tests/server/DBoGenerated.d.ts +70 -0
  86. package/tests/server/index.js +29 -2
  87. package/tests/server/index.ts +30 -4
  88. package/tests/server/init.sql +25 -0
  89. package/tests/server/package-lock.json +5 -5
  90. package/tests/config_test2/DBoGenerated.d.ts +0 -135
  91. package/tests/config_test2/tsconfig.json +0 -21
@@ -5,7 +5,7 @@
5
5
  *--------------------------------------------------------------------------------------------*/
6
6
  var _a;
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.postgresToTsType = exports.isPlainObject = exports.DboBuilder = exports.TableHandler = exports.ViewHandler = exports.EXISTS_KEYS = exports.escapeTSNames = exports.pgp = exports.getUpdateFilter = void 0;
8
+ exports.postgresToTsType = exports.isPlainObject = exports.DboBuilder = exports.TableHandler = exports.isPojoObject = exports.ViewHandler = exports.parseError = exports.EXISTS_KEYS = exports.makeErr = exports.escapeTSNames = exports.pgp = exports.getUpdateFilter = void 0;
9
9
  const Bluebird = require("bluebird");
10
10
  // declare global { export interface Promise<T> extends Bluebird<T> {} }
11
11
  const pgPromise = require("pg-promise");
@@ -25,6 +25,8 @@ exports.getUpdateFilter = getUpdateFilter;
25
25
  const utils_1 = require("./utils");
26
26
  const QueryBuilder_1 = require("./QueryBuilder");
27
27
  const PubSubManager_1 = require("./PubSubManager");
28
+ const insertDataParse_1 = require("./DboBuilder/insertDataParse");
29
+ const insert_1 = require("./DboBuilder/insert");
28
30
  const Filtering_1 = require("./Filtering");
29
31
  exports.pgp = pgPromise({
30
32
  promiseLib: Bluebird
@@ -96,15 +98,17 @@ function makeErr(err, localParams, view, allowedKeys) {
96
98
  }
97
99
  return Promise.reject(errObject);
98
100
  }
101
+ exports.makeErr = makeErr;
99
102
  exports.EXISTS_KEYS = ["$exists", "$notExists", "$existsJoined", "$notExistsJoined"];
100
103
  const FILTER_FUNCS = QueryBuilder_1.FUNCTIONS.filter(f => f.canBeUsedForFilter);
101
104
  function parseError(e) {
102
105
  // console.trace("INTERNAL ERROR: ", e);
103
- let res = (!Object.keys(e || {}).length ? e : (e && e.toString) ? e.toString() : e);
106
+ let res = e instanceof Error ? e.message : (!Object.keys(e || {}).length ? e : (e && e.toString) ? e.toString() : e);
104
107
  if (isPlainObject(e))
105
108
  res = JSON.stringify(e, null, 2);
106
109
  return res;
107
110
  }
111
+ exports.parseError = parseError;
108
112
  class ColSet {
109
113
  constructor(columns, tableName) {
110
114
  this.opts = { columns, tableName, colNames: columns.map(c => c.name) };
@@ -292,12 +296,15 @@ class ViewHandler {
292
296
  // let searchedTables = [], result;
293
297
  // while (!result && searchedTables.length <= this.joins.length * 2){
294
298
  // }
299
+ const getJoinCondition = (on, leftTable, rightTable) => {
300
+ return on.map(cond => Object.keys(cond).map(lKey => `${leftTable}.${lKey} = ${rightTable}.${cond[lKey]}`).join("\nAND ")).join(" OR ");
301
+ };
295
302
  let toOne = true, query = this.joins.map(({ tables, on, type }, i) => {
296
303
  if (type.split("-")[1] === "many") {
297
304
  toOne = false;
298
305
  }
299
306
  const tl = `tl${startAlias + i}`, tr = `tr${startAlias + i}`;
300
- return `FROM ${tables[0]} ${tl} ${isInner ? "INNER" : "LEFT"} JOIN ${tables[1]} ${tr} ON ${Object.keys(on).map(lKey => `${tl}.${lKey} = ${tr}.${on[lKey]}`).join("\nAND ")}`;
307
+ return `FROM ${tables[0]} ${tl} ${isInner ? "INNER" : "LEFT"} JOIN ${tables[1]} ${tr} ON ${getJoinCondition(on, tl, tr)}`;
301
308
  }).join("\n");
302
309
  return { query, toOne: false };
303
310
  }
@@ -337,16 +344,20 @@ class ViewHandler {
337
344
  throw `Joining ${t1} <-> ${t2} dissallowed or missing`;
338
345
  ;
339
346
  let on = [];
340
- Object.keys(jo.on).map(leftKey => {
341
- const rightKey = jo.on[leftKey];
342
- /* Left table is joining on keys */
343
- if (jo.tables[0] === t1) {
344
- on.push([leftKey, rightKey]);
345
- /* Left table is joining on values */
346
- }
347
- else {
348
- on.push([rightKey, leftKey]);
349
- }
347
+ jo.on.map(cond => {
348
+ let condArr = [];
349
+ Object.keys(cond).map(leftKey => {
350
+ const rightKey = cond[leftKey];
351
+ /* Left table is joining on keys */
352
+ if (jo.tables[0] === t1) {
353
+ condArr.push([leftKey, rightKey]);
354
+ /* Left table is joining on values */
355
+ }
356
+ else {
357
+ condArr.push([rightKey, leftKey]);
358
+ }
359
+ });
360
+ on.push(condArr);
350
361
  });
351
362
  return {
352
363
  source,
@@ -676,7 +687,8 @@ class ViewHandler {
676
687
  // console.trace(e)
677
688
  if (localParams && localParams.testRule)
678
689
  throw e;
679
- throw { err: parseError(e), msg: `Issue with dbo.${this.name}.find(${JSON.stringify(filter || {}, null, 2)}, ${JSON.stringify(selectParams || {}, null, 2)})` };
690
+ // ${JSON.stringify(filter || {}, null, 2)}, ${JSON.stringify(selectParams || {}, null, 2)}
691
+ throw { err: parseError(e), msg: `Issue with dbo.${this.name}.find()`, args: { filter, selectParams } };
680
692
  }
681
693
  }
682
694
  findOne(filter, selectParams, param3_unused, table_rules, localParams) {
@@ -865,7 +877,9 @@ class ViewHandler {
865
877
  let table = paths[ji].table;
866
878
  let tableAlias = (0, prostgles_types_1.asName)(ji < paths.length - 1 ? `jd${ji}` : table);
867
879
  let prevTableAlias = (0, prostgles_types_1.asName)(ji ? `jd${ji - 1}` : thisTable);
868
- let cond = `${jp.on.map(([c1, c2]) => `${prevTableAlias}.${(0, prostgles_types_1.asName)(c1)} = ${tableAlias}.${(0, prostgles_types_1.asName)(c2)}`).join("\n AND ")}`;
880
+ let cond = `${jp.on.map(c => {
881
+ return c.map(([c1, c2]) => `${prevTableAlias}.${(0, prostgles_types_1.asName)(c1)} = ${tableAlias}.${(0, prostgles_types_1.asName)(c2)}`).join(" AND ");
882
+ }).join("\n OR ")}`;
869
883
  let j = `SELECT 1 \n` +
870
884
  `FROM ${(0, prostgles_types_1.asName)(table)} ${tableAlias} \n` +
871
885
  `WHERE ${cond} \n`; //
@@ -1393,9 +1407,11 @@ function isPojoObject(obj) {
1393
1407
  }
1394
1408
  return true;
1395
1409
  }
1410
+ exports.isPojoObject = isPojoObject;
1396
1411
  class TableHandler extends ViewHandler {
1397
1412
  constructor(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths) {
1398
1413
  super(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths);
1414
+ this.insertDataParse = insertDataParse_1.insertDataParse;
1399
1415
  this.prepareReturning = async (returning, allowedFields) => {
1400
1416
  let result = [];
1401
1417
  if (returning) {
@@ -1717,321 +1733,9 @@ class TableHandler extends ViewHandler {
1717
1733
  });
1718
1734
  return { data, allowedCols: this.columns.filter(c => dataKeys.includes(c.name)).map(c => c.name) };
1719
1735
  }
1720
- async insertDataParse(data, param2, param3_unused, tableRules, _localParams) {
1721
- const localParams = _localParams || {};
1722
- let dbTX = localParams?.dbTX || this.dbTX;
1723
- const isMultiInsert = Array.isArray(data);
1724
- const getExtraKeys = (d) => Object.keys(d).filter(k => !this.columns.find(c => c.name === k));
1725
- /* Nested insert is not allowed for the file table */
1726
- const isNestedInsert = this.is_media ? false : (Array.isArray(data) ? data : [data]).some(d => getExtraKeys(d).length);
1727
- /**
1728
- * Make sure nested insert uses a transaction
1729
- */
1730
- if (isNestedInsert && !dbTX) {
1731
- return {
1732
- insertResult: await this.dboBuilder.getTX((dbTX) => dbTX[this.name].insert(data, param2, param3_unused, tableRules, { dbTX, ...localParams }))
1733
- };
1734
- }
1735
- // if(!dbTX && this.t) dbTX = this.d;
1736
- const preValidate = tableRules?.insert?.preValidate, validate = tableRules?.insert?.validate;
1737
- let _data = await Promise.all((Array.isArray(data) ? data : [data]).map(async (row) => {
1738
- if (preValidate) {
1739
- row = await preValidate(row);
1740
- }
1741
- const dataKeys = Object.keys(row);
1742
- const extraKeys = getExtraKeys(row);
1743
- /* Upload file then continue insert */
1744
- if (this.is_media) {
1745
- if (!this.dboBuilder.prostgles?.fileManager)
1746
- throw "fileManager not set up";
1747
- const { data, name } = row;
1748
- if (dataKeys.length !== 2)
1749
- throw "Expecting only two properties: { name: string; data: File }";
1750
- // if(!Buffer.isBuffer(data)) throw "data is not of type Buffer"
1751
- if (!data)
1752
- throw "data not provided";
1753
- if (typeof name !== "string") {
1754
- throw "name is not of type string";
1755
- }
1756
- const media_id = (await this.db.oneOrNone("SELECT gen_random_uuid() as name")).name;
1757
- const type = await this.dboBuilder.prostgles.fileManager.getMIME(data, name);
1758
- const media_name = `${media_id}.${type.ext}`;
1759
- let media = {
1760
- id: media_id,
1761
- name: media_name,
1762
- original_name: name,
1763
- extension: type.ext,
1764
- content_type: type.mime
1765
- };
1766
- if (validate) {
1767
- media = await validate(media);
1768
- }
1769
- const _media = await this.dboBuilder.prostgles.fileManager.uploadAsMedia({
1770
- item: {
1771
- data,
1772
- name: media.name ?? "????",
1773
- content_type: media.content_type
1774
- },
1775
- // imageCompression: {
1776
- // inside: {
1777
- // width: 1100,
1778
- // height: 630
1779
- // }
1780
- // }
1781
- });
1782
- return {
1783
- ...media,
1784
- ..._media,
1785
- };
1786
- /* Potentially a nested join */
1787
- }
1788
- else if (extraKeys.length) {
1789
- /* Ensure we're using the same transaction */
1790
- const _this = this.t ? this : dbTX[this.name];
1791
- let rootData = Array.isArray(data) ? data.map(d => (0, PubSubManager_1.omitKeys)(d, extraKeys)) : (0, PubSubManager_1.omitKeys)(data, extraKeys);
1792
- let insertedChildren;
1793
- let targetTableRules;
1794
- const fullRootResult = await _this.insert(rootData, { returning: "*" }, undefined, tableRules, localParams);
1795
- let returnData;
1796
- const returning = param2?.returning;
1797
- if (returning) {
1798
- returnData = {};
1799
- const returningItems = await this.prepareReturning(returning, this.parseFieldFilter(tableRules?.insert?.returningFields));
1800
- returningItems.filter(s => s.selected).map(rs => {
1801
- returnData[rs.alias] = fullRootResult[rs.alias];
1802
- });
1803
- }
1804
- await Promise.all(extraKeys.map(async (targetTable) => {
1805
- const childDataItems = Array.isArray(row[targetTable]) ? row[targetTable] : [row[targetTable]];
1806
- /* Must be allowed to insert into media table */
1807
- const canInsert = async (tbl) => {
1808
- const childRules = await this.dboBuilder.publishParser?.getValidatedRequestRuleWusr({ tableName: tbl, command: "insert", localParams });
1809
- if (!childRules || !childRules.insert)
1810
- throw "Dissallowed nested insert into table " + childRules;
1811
- return childRules;
1812
- };
1813
- // console.log(JSON.stringify(this.dboBuilder.joinPaths, null, 2))
1814
- const jp = this.dboBuilder.joinPaths.find(jp => jp.t1 === this.name && jp.t2 === targetTable);
1815
- if (!jp)
1816
- throw `Could not find a valid table for the nested data { ${targetTable} } `;
1817
- const thisInfo = await this.getInfo();
1818
- const childInsert = async (cdata, tableName) => {
1819
- // console.log("childInsert", {data, tableName})
1820
- if (!cdata || !dbTX?.[tableName] || !("insert" in dbTX[tableName]))
1821
- throw "childInsertErr: Child table handler missing for: " + tableName;
1822
- const tableRules = await canInsert(tableName);
1823
- if (thisInfo.has_media === "one" && thisInfo.media_table_name === tableName && Array.isArray(cdata) && cdata.length > 1) {
1824
- throw "Constraint check fail: Cannot insert more than one record into " + JSON.stringify(tableName);
1825
- }
1826
- return Promise.all((Array.isArray(cdata) ? cdata : [cdata])
1827
- .map(m => dbTX[tableName]
1828
- .insert(m, { returning: "*" }, undefined, tableRules, localParams)
1829
- .catch(e => {
1830
- console.trace({ childInsertErr: e });
1831
- return Promise.reject({ childInsertErr: e });
1832
- })));
1833
- };
1834
- const { path } = jp;
1835
- const [tbl1, tbl2, tbl3] = path;
1836
- targetTableRules = await canInsert(targetTable); // tbl3
1837
- const cols2 = this.dboBuilder.dbo[tbl2].columns || [];
1838
- if (!this.dboBuilder.dbo[tbl2])
1839
- throw "Invalid/disallowed table: " + tbl2;
1840
- const colsRefT1 = cols2?.filter(c => c.references?.cols.length === 1 && c.references?.ftable === tbl1);
1841
- if (!path.length) {
1842
- throw "Nested inserts join path not found for " + [this.name, targetTable];
1843
- }
1844
- else if (path.length === 2) {
1845
- if (targetTable !== tbl2)
1846
- throw "Did not expect this";
1847
- if (!colsRefT1.length)
1848
- throw `Target table ${tbl2} does not reference any columns from the root table ${this.name}. Cannot do nested insert`;
1849
- // console.log(childDataItems, JSON.stringify(colsRefT1, null, 2))
1850
- insertedChildren = await childInsert(childDataItems.map((d) => {
1851
- let result = { ...d };
1852
- colsRefT1.map(col => {
1853
- result[col.references.cols[0]] = fullRootResult[col.references.fcols[0]];
1854
- });
1855
- return result;
1856
- }), targetTable);
1857
- // console.log({ insertedChildren })
1858
- }
1859
- else if (path.length === 3) {
1860
- if (targetTable !== tbl3)
1861
- throw "Did not expect this";
1862
- const colsRefT3 = cols2?.filter(c => c.references?.cols.length === 1 && c.references?.ftable === tbl3);
1863
- if (!colsRefT1.length || !colsRefT3.length)
1864
- throw "Incorrectly referenced or missing columns for nested insert";
1865
- if (targetTable !== this.dboBuilder.prostgles.fileManager?.tableName) {
1866
- throw "Only media allowed to have nested inserts more than 2 tables apart";
1867
- }
1868
- /* We expect tbl2 to have only 2 columns (media_id and foreign_id) */
1869
- if (!cols2 || cols2.find(c => !["media_id", "foreign_id"].includes(c.name))) {
1870
- throw "Second joining table not of expected format";
1871
- }
1872
- insertedChildren = await childInsert(childDataItems, targetTable);
1873
- /* Insert in key_lookup table */
1874
- await Promise.all(insertedChildren.map(async (t3Child) => {
1875
- let tbl2Row = {};
1876
- colsRefT3.map(col => {
1877
- tbl2Row[col.name] = t3Child[col.references.fcols[0]];
1878
- });
1879
- colsRefT1.map(col => {
1880
- tbl2Row[col.name] = fullRootResult[col.references.fcols[0]];
1881
- });
1882
- // console.log({ rootResult, tbl2Row, t3Child, colsRefT3, colsRefT1, t: this.t?.ctx?.start });
1883
- await childInsert(tbl2Row, tbl2); //.then(() => {});
1884
- }));
1885
- }
1886
- else {
1887
- console.error(JSON.stringify({ path, thisTable: this.name, targetTable }, null, 2));
1888
- throw "Unexpected path for Nested inserts";
1889
- }
1890
- /* Return also the nested inserted data */
1891
- if (targetTableRules && insertedChildren?.length && returning) {
1892
- const targetTableHandler = dbTX[targetTable];
1893
- const targetReturning = await targetTableHandler.prepareReturning("*", targetTableHandler.parseFieldFilter(targetTableRules?.insert?.returningFields));
1894
- let clientTargetInserts = insertedChildren.map(d => {
1895
- let _d = { ...d };
1896
- let res = {};
1897
- targetReturning.map(r => {
1898
- res[r.alias] = _d[r.alias];
1899
- });
1900
- return res;
1901
- });
1902
- returnData[targetTable] = clientTargetInserts.length === 1 ? clientTargetInserts[0] : clientTargetInserts;
1903
- }
1904
- }));
1905
- return returnData;
1906
- }
1907
- return row;
1908
- }));
1909
- let result = isMultiInsert ? _data : _data[0];
1910
- // if(validate && !isNestedInsert){
1911
- // result = isMultiInsert? await Promise.all(_data.map(async d => await validate({ ...d }))) : await validate({ ..._data[0] });
1912
- // }
1913
- let res = isNestedInsert ?
1914
- { insertResult: result } :
1915
- { data: result };
1916
- return res;
1917
- }
1918
1736
  async insert(rowOrRows, param2, param3_unused, tableRules, _localParams) {
1919
- const localParams = _localParams || {};
1920
- const { dbTX } = localParams;
1921
- try {
1922
- const { returning, onConflictDoNothing, fixIssues = false } = param2 || {};
1923
- const { testRule = false, returnQuery = false } = localParams || {};
1924
- let returningFields, forcedData, fields;
1925
- if (tableRules) {
1926
- if (!tableRules.insert)
1927
- throw "insert rules missing for " + this.name;
1928
- returningFields = tableRules.insert.returningFields;
1929
- forcedData = tableRules.insert.forcedData;
1930
- fields = tableRules.insert.fields;
1931
- /* If no returning fields specified then take select fields as returning */
1932
- if (!returningFields)
1933
- returningFields = (0, utils_1.get)(tableRules, "select.fields") || (0, utils_1.get)(tableRules, "insert.fields");
1934
- if (!fields)
1935
- throw ` invalid insert rule for ${this.name} -> fields missing `;
1936
- /* Safely test publish rules */
1937
- if (testRule) {
1938
- // if(this.is_media && tableRules.insert.preValidate) throw "Media table cannot have a preValidate. It already is used internally by prostgles for file upload";
1939
- await this.validateViewRules({ fields, returningFields, forcedFilter: forcedData, rule: "insert" });
1940
- if (forcedData) {
1941
- const keys = Object.keys(forcedData);
1942
- if (keys.length) {
1943
- try {
1944
- const colset = new exports.pgp.helpers.ColumnSet(this.columns.filter(c => keys.includes(c.name)).map(c => ({ name: c.name, cast: c.udt_name === "uuid" ? c.udt_name : undefined }))), values = exports.pgp.helpers.values(forcedData, colset), colNames = this.prepareSelect(keys, this.column_names);
1945
- await this.db.any("EXPLAIN INSERT INTO " + this.escapedName + " (${colNames:raw}) SELECT * FROM ( VALUES ${values:raw} ) t WHERE FALSE;", { colNames, values });
1946
- }
1947
- catch (e) {
1948
- throw "\nissue with forcedData: \nVALUE: " + JSON.stringify(forcedData, null, 2) + "\nERROR: " + e;
1949
- }
1950
- }
1951
- }
1952
- return true;
1953
- }
1954
- }
1955
- let conflict_query = "";
1956
- if (typeof onConflictDoNothing === "boolean" && onConflictDoNothing) {
1957
- conflict_query = " ON CONFLICT DO NOTHING ";
1958
- }
1959
- if (param2) {
1960
- const good_params = ["returning", "multi", "onConflictDoNothing", "fixIssues"];
1961
- const bad_params = Object.keys(param2).filter(k => !good_params.includes(k));
1962
- if (bad_params && bad_params.length)
1963
- throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", ");
1964
- }
1965
- if (!rowOrRows)
1966
- rowOrRows = {}; //throw "Provide data in param1";
1967
- let returningSelect = this.makeReturnQuery(await this.prepareReturning(returning, this.parseFieldFilter(returningFields)));
1968
- const makeQuery = async (_row, isOne = false) => {
1969
- let row = { ..._row };
1970
- if (!isPojoObject(row)) {
1971
- console.trace(row);
1972
- throw "\ninvalid insert data provided -> " + JSON.stringify(row);
1973
- }
1974
- const { data, allowedCols } = this.validateNewData({ row, forcedData, allowedFields: fields, tableRules, fixIssues });
1975
- let _data = { ...data };
1976
- let insertQ = "";
1977
- if (!Object.keys(_data).length)
1978
- insertQ = `INSERT INTO ${(0, prostgles_types_1.asName)(this.name)} DEFAULT VALUES `;
1979
- else
1980
- insertQ = await this.colSet.getInsertQuery(_data, allowedCols, tableRules?.insert?.validate); // pgp.helpers.insert(_data, columnSet);
1981
- return insertQ + conflict_query + returningSelect;
1982
- };
1983
- let query = "";
1984
- let queryType = "none";
1985
- /**
1986
- * If media it will: upload file and continue insert
1987
- * If nested insert it will: make separate inserts and not continue main insert
1988
- */
1989
- const insRes = await this.insertDataParse(rowOrRows, param2, param3_unused, tableRules, localParams);
1990
- const { data, insertResult } = insRes;
1991
- if ("insertResult" in insRes) {
1992
- return insertResult;
1993
- }
1994
- if (Array.isArray(data)) {
1995
- // if(returning) throw "Sorry but [returning] is dissalowed for multi insert";
1996
- let queries = await Promise.all(data.map(async (p) => {
1997
- const q = await makeQuery(p);
1998
- return q;
1999
- }));
2000
- query = exports.pgp.helpers.concat(queries);
2001
- if (returning)
2002
- queryType = "many";
2003
- }
2004
- else {
2005
- query = await makeQuery(data, true);
2006
- if (returning)
2007
- queryType = "one";
2008
- }
2009
- if (returnQuery)
2010
- return query;
2011
- let result;
2012
- if (this.dboBuilder.prostgles.opts.DEBUG_MODE) {
2013
- console.log(this.t?.ctx?.start, "insert in " + this.name, data);
2014
- }
2015
- const tx = dbTX?.[this.name]?.t || this.t;
2016
- const allowedFieldKeys = this.parseFieldFilter(fields);
2017
- if (tx) {
2018
- result = tx[queryType](query).catch((err) => makeErr(err, localParams, this, allowedFieldKeys));
2019
- }
2020
- else {
2021
- result = this.db.tx(t => t[queryType](query)).catch(err => makeErr(err, localParams, this, allowedFieldKeys));
2022
- }
2023
- return result;
2024
- }
2025
- catch (e) {
2026
- if (localParams && localParams.testRule)
2027
- throw e;
2028
- throw { err: parseError(e), msg: `Issue with dbo.${this.name}.insert(
2029
- ${JSON.stringify(rowOrRows || {}, null, 2)},
2030
- ${JSON.stringify(param2 || {}, null, 2)}
2031
- )` };
2032
- }
1737
+ return insert_1.insert.bind(this)(rowOrRows, param2, param3_unused, tableRules, _localParams);
2033
1738
  }
2034
- ;
2035
1739
  makeReturnQuery(items) {
2036
1740
  if (items?.length)
2037
1741
  return " RETURNING " + items.map(s => s.getQuery() + " AS " + (0, prostgles_types_1.asName)(s.alias)).join(", ");
@@ -2229,9 +1933,9 @@ class DboBuilder {
2229
1933
  this.getPubSubManager = async () => {
2230
1934
  if (!this._pubSubManager) {
2231
1935
  let onSchemaChange;
2232
- if (this.prostgles.opts.watchSchema && this.prostgles.opts.watchSchemaType === "events") {
1936
+ if (this.prostgles.opts.watchSchema && this.prostgles.opts.watchSchemaType === "DDL_trigger") {
2233
1937
  if (!this.prostgles.isSuperUser) {
2234
- console.warn(`watchSchemaType "events" cannot be used because db user is not a superuser. Will fallback to watchSchemaType "queries" `);
1938
+ console.warn(`watchSchemaType "events" cannot be used because db user is not a superuser. Will fallback to watchSchemaType "prostgles_queries" `);
2235
1939
  }
2236
1940
  else {
2237
1941
  onSchemaChange = (event) => {
@@ -2251,15 +1955,17 @@ class DboBuilder {
2251
1955
  console.warn(`subscribe and sync cannot be used because db user is not a superuser `);
2252
1956
  }
2253
1957
  }
2254
- if (!this._pubSubManager)
1958
+ if (!this._pubSubManager) {
1959
+ console.trace("Could not create this._pubSubManager");
2255
1960
  throw "Could not create this._pubSubManager";
1961
+ }
2256
1962
  return this._pubSubManager;
2257
1963
  };
2258
1964
  this.joinPaths = [];
2259
1965
  this.init = async () => {
2260
1966
  /* If watchSchema then PubSubManager must be created */
2261
1967
  await this.build();
2262
- if (this.prostgles.opts.watchSchema) {
1968
+ if (this.prostgles.opts.watchSchema && (this.prostgles.opts.watchSchemaType === "DDL_trigger" || !this.prostgles.opts.watchSchemaType) && this.prostgles.isSuperUser) {
2263
1969
  await this.getPubSubManager();
2264
1970
  }
2265
1971
  return this;
@@ -2307,8 +2013,10 @@ class DboBuilder {
2307
2013
  async parseJoins() {
2308
2014
  if (this.prostgles.opts.joins) {
2309
2015
  let _joins = await this.prostgles.opts.joins;
2310
- let inferredJoins = await getInferredJoins(this.db, this.prostgles.opts.schema);
2311
- if (typeof _joins === "string" && _joins === "inferred") {
2016
+ if (!this.tablesOrViews)
2017
+ throw new Error("Could not create join config. this.tablesOrViews missing");
2018
+ let inferredJoins = await getInferredJoins2(this.tablesOrViews);
2019
+ if (_joins === "inferred") {
2312
2020
  _joins = inferredJoins;
2313
2021
  /* If joins are specified then include inferred joins except the explicit tables */
2314
2022
  }
@@ -2316,6 +2024,9 @@ class DboBuilder {
2316
2024
  const joinTables = _joins.map(j => j.tables).flat();
2317
2025
  _joins = _joins.concat(inferredJoins.filter(j => !j.tables.find(t => joinTables.includes(t))));
2318
2026
  }
2027
+ else if (_joins) {
2028
+ throw new Error("Unexpected joins init param. Expecting 'inferred' OR joinConfig but got: " + JSON.stringify(_joins));
2029
+ }
2319
2030
  let joins = JSON.parse(JSON.stringify(_joins));
2320
2031
  this.joins = joins;
2321
2032
  // Validate joins
@@ -2518,11 +2229,16 @@ class DboBuilder {
2518
2229
  */
2519
2230
  const { watchSchema, watchSchemaType } = this.prostgles?.opts || {};
2520
2231
  if (watchSchema &&
2521
- (!this.prostgles.isSuperUser || watchSchemaType === "queries") &&
2522
- (["CREATE", "ALTER", "DROP"].includes(command) ||
2523
- // Cover this case: `CREATE TABLE mytable AS SELECT`
2524
- query && query.toLowerCase().replace(/\s\s+/g, ' ').includes("create table"))) {
2525
- this.prostgles.onSchemaChange({ command, query });
2232
+ (!this.prostgles.isSuperUser || watchSchemaType === "prostgles_queries")) {
2233
+ if (["CREATE", "ALTER", "DROP"].includes(command)) {
2234
+ this.prostgles.onSchemaChange({ command, query });
2235
+ }
2236
+ else if (query) {
2237
+ const cleanedQuery = query.toLowerCase().replace(/\s\s+/g, ' ');
2238
+ if (PubSubManager_1.PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase()))) {
2239
+ this.prostgles.onSchemaChange({ command, query });
2240
+ }
2241
+ }
2526
2242
  }
2527
2243
  if (command === "LISTEN") {
2528
2244
  if (!allowListen)
@@ -3012,51 +2728,82 @@ function sqlErrCodeToMsg(code) {
3012
2728
  JSON.stringify([...THE_table_$0.rows].map(t => [...t.children].map(u => u.innerText)).filter((d, i) => i && d.length > 1).reduce((a, v)=>({ ...a, [v[0]]: v[1] }), {}))
3013
2729
  */
3014
2730
  }
3015
- async function getInferredJoins(db, schema = "public") {
2731
+ async function getInferredJoins2(schema) {
3016
2732
  let joins = [];
3017
- let res = await db.any(`SELECT
3018
- tc.table_schema,
3019
- tc.constraint_name,
3020
- tc.table_name,
3021
- kcu.column_name,
3022
- ccu.table_schema AS foreign_table_schema,
3023
- ccu.table_name AS foreign_table_name,
3024
- ccu.column_name AS foreign_column_name,
3025
- tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') as foreign_is_unique
3026
- FROM
3027
- information_schema.table_constraints AS tc
3028
- JOIN information_schema.key_column_usage AS kcu
3029
- ON tc.constraint_name = kcu.constraint_name
3030
- AND tc.table_schema = kcu.table_schema
3031
- JOIN information_schema.constraint_column_usage AS ccu
3032
- ON ccu.constraint_name = tc.constraint_name
3033
- AND ccu.table_schema = tc.table_schema
3034
- WHERE tc.table_schema=` + "${schema}" + `
3035
- AND tc.constraint_type = 'FOREIGN KEY'
3036
- AND tc.table_name <> ccu.table_name -- Exclude self-referencing tables
3037
- `, { schema });
3038
- res.map((d) => {
3039
- let eIdx = joins.findIndex(j => j.tables.includes(d.table_name) && j.tables.includes(d.foreign_table_name));
3040
- let existing = joins[eIdx];
2733
+ const upsertJoin = (t1, t2, cols) => {
2734
+ let existingIdx = joins.findIndex(j => j.tables.slice(0).sort().join() === [t1, t2].sort().join());
2735
+ let existing = joins[existingIdx];
2736
+ const normalCond = cols.reduce((a, v) => ({ ...a, [v.col1]: v.col2 }), {});
2737
+ const revertedCond = cols.reduce((a, v) => ({ ...a, [v.col2]: v.col1 }), {});
3041
2738
  if (existing) {
3042
- if (existing.tables[0] === d.table_name) {
3043
- existing.on = { ...existing.on, [d.column_name]: d.foreign_column_name };
3044
- }
3045
- else {
3046
- existing.on = { ...existing.on, [d.foreign_column_name]: d.column_name };
2739
+ const cond = existing.tables[0] === t1 ? normalCond : revertedCond;
2740
+ /** Avoid duplicates */
2741
+ if (!existing.on.some(_cond => JSON.stringify(_cond) === JSON.stringify(cond))) {
2742
+ existing.on.push(cond);
2743
+ joins[existingIdx] = existing;
3047
2744
  }
3048
- joins[eIdx] = existing;
3049
2745
  }
3050
2746
  else {
3051
2747
  joins.push({
3052
- tables: [d.table_name, d.foreign_table_name],
3053
- on: {
3054
- [d.column_name]: d.foreign_column_name
3055
- },
2748
+ tables: [t1, t2],
2749
+ on: [normalCond],
3056
2750
  type: "many-many"
3057
2751
  });
3058
2752
  }
2753
+ };
2754
+ schema.map(tov => {
2755
+ tov.columns.map(col => {
2756
+ if (col.references) {
2757
+ const r = col.references;
2758
+ upsertJoin(tov.name, r.ftable, r.cols.map((c, i) => ({ col1: c, col2: r.fcols[i] })));
2759
+ }
2760
+ });
3059
2761
  });
3060
2762
  return joins;
3061
2763
  }
2764
+ // async function getInferredJoins(db: DB, schema: string = "public"): Promise<Join[]>{
2765
+ // let joins: Join[] = [];
2766
+ // let res = await db.any(`SELECT
2767
+ // tc.table_schema,
2768
+ // tc.constraint_name,
2769
+ // tc.table_name,
2770
+ // kcu.column_name,
2771
+ // ccu.table_schema AS foreign_table_schema,
2772
+ // ccu.table_name AS foreign_table_name,
2773
+ // ccu.column_name AS foreign_column_name,
2774
+ // tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') as foreign_is_unique
2775
+ // FROM
2776
+ // information_schema.table_constraints AS tc
2777
+ // JOIN information_schema.key_column_usage AS kcu
2778
+ // ON tc.constraint_name = kcu.constraint_name
2779
+ // AND tc.table_schema = kcu.table_schema
2780
+ // JOIN information_schema.constraint_column_usage AS ccu
2781
+ // ON ccu.constraint_name = tc.constraint_name
2782
+ // AND ccu.table_schema = tc.table_schema
2783
+ // WHERE tc.table_schema=` + "${schema}" + `
2784
+ // AND tc.constraint_type = 'FOREIGN KEY'
2785
+ // AND tc.table_name <> ccu.table_name -- Exclude self-referencing tables
2786
+ // `, { schema });
2787
+ // res.map((d: any) => {
2788
+ // let eIdx = joins.findIndex(j => j.tables.includes(d.table_name) && j.tables.includes(d.foreign_table_name));
2789
+ // let existing = joins[eIdx];
2790
+ // if(existing){
2791
+ // if(existing.tables[0] === d.table_name){
2792
+ // existing.on = { ...existing.on, [d.column_name]: d.foreign_column_name }
2793
+ // } else {
2794
+ // existing.on = { ...existing.on, [d.foreign_column_name]: d.column_name }
2795
+ // }
2796
+ // joins[eIdx] = existing;
2797
+ // } else {
2798
+ // joins.push({
2799
+ // tables: [d.table_name, d.foreign_table_name],
2800
+ // on: {
2801
+ // [d.column_name]: d.foreign_column_name
2802
+ // },
2803
+ // type: "many-many"
2804
+ // })
2805
+ // }
2806
+ // });
2807
+ // return joins;
2808
+ // }
3062
2809
  //# sourceMappingURL=DboBuilder.js.map