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
package/lib/DboBuilder.js CHANGED
@@ -28,7 +28,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
28
28
  };
29
29
  var _a;
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.postgresToTsType = exports.isPlainObject = exports.DboBuilder = exports.TableHandler = exports.ViewHandler = exports.EXISTS_KEYS = exports.escapeTSNames = exports.pgp = exports.getUpdateFilter = void 0;
31
+ 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;
32
32
  const Bluebird = __importStar(require("bluebird"));
33
33
  // declare global { export interface Promise<T> extends Bluebird<T> {} }
34
34
  const pgPromise = __importStar(require("pg-promise"));
@@ -48,6 +48,8 @@ exports.getUpdateFilter = getUpdateFilter;
48
48
  const utils_1 = require("./utils");
49
49
  const QueryBuilder_1 = require("./QueryBuilder");
50
50
  const PubSubManager_1 = require("./PubSubManager");
51
+ const insertDataParse_1 = require("./DboBuilder/insertDataParse");
52
+ const insert_1 = require("./DboBuilder/insert");
51
53
  const Filtering_1 = require("./Filtering");
52
54
  exports.pgp = pgPromise({
53
55
  promiseLib: Bluebird
@@ -119,15 +121,17 @@ function makeErr(err, localParams, view, allowedKeys) {
119
121
  }
120
122
  return Promise.reject(errObject);
121
123
  }
124
+ exports.makeErr = makeErr;
122
125
  exports.EXISTS_KEYS = ["$exists", "$notExists", "$existsJoined", "$notExistsJoined"];
123
126
  const FILTER_FUNCS = QueryBuilder_1.FUNCTIONS.filter(f => f.canBeUsedForFilter);
124
127
  function parseError(e) {
125
128
  // console.trace("INTERNAL ERROR: ", e);
126
- let res = (!Object.keys(e || {}).length ? e : (e && e.toString) ? e.toString() : e);
129
+ let res = e instanceof Error ? e.message : (!Object.keys(e || {}).length ? e : (e && e.toString) ? e.toString() : e);
127
130
  if (isPlainObject(e))
128
131
  res = JSON.stringify(e, null, 2);
129
132
  return res;
130
133
  }
134
+ exports.parseError = parseError;
131
135
  class ColSet {
132
136
  constructor(columns, tableName) {
133
137
  this.opts = { columns, tableName, colNames: columns.map(c => c.name) };
@@ -315,12 +319,15 @@ class ViewHandler {
315
319
  // let searchedTables = [], result;
316
320
  // while (!result && searchedTables.length <= this.joins.length * 2){
317
321
  // }
322
+ const getJoinCondition = (on, leftTable, rightTable) => {
323
+ return on.map(cond => Object.keys(cond).map(lKey => `${leftTable}.${lKey} = ${rightTable}.${cond[lKey]}`).join("\nAND ")).join(" OR ");
324
+ };
318
325
  let toOne = true, query = this.joins.map(({ tables, on, type }, i) => {
319
326
  if (type.split("-")[1] === "many") {
320
327
  toOne = false;
321
328
  }
322
329
  const tl = `tl${startAlias + i}`, tr = `tr${startAlias + i}`;
323
- 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 ")}`;
330
+ return `FROM ${tables[0]} ${tl} ${isInner ? "INNER" : "LEFT"} JOIN ${tables[1]} ${tr} ON ${getJoinCondition(on, tl, tr)}`;
324
331
  }).join("\n");
325
332
  return { query, toOne: false };
326
333
  }
@@ -360,16 +367,20 @@ class ViewHandler {
360
367
  throw `Joining ${t1} <-> ${t2} dissallowed or missing`;
361
368
  ;
362
369
  let on = [];
363
- Object.keys(jo.on).map(leftKey => {
364
- const rightKey = jo.on[leftKey];
365
- /* Left table is joining on keys */
366
- if (jo.tables[0] === t1) {
367
- on.push([leftKey, rightKey]);
368
- /* Left table is joining on values */
369
- }
370
- else {
371
- on.push([rightKey, leftKey]);
372
- }
370
+ jo.on.map(cond => {
371
+ let condArr = [];
372
+ Object.keys(cond).map(leftKey => {
373
+ const rightKey = cond[leftKey];
374
+ /* Left table is joining on keys */
375
+ if (jo.tables[0] === t1) {
376
+ condArr.push([leftKey, rightKey]);
377
+ /* Left table is joining on values */
378
+ }
379
+ else {
380
+ condArr.push([rightKey, leftKey]);
381
+ }
382
+ });
383
+ on.push(condArr);
373
384
  });
374
385
  return {
375
386
  source,
@@ -699,7 +710,8 @@ class ViewHandler {
699
710
  // console.trace(e)
700
711
  if (localParams && localParams.testRule)
701
712
  throw e;
702
- throw { err: parseError(e), msg: `Issue with dbo.${this.name}.find(${JSON.stringify(filter || {}, null, 2)}, ${JSON.stringify(selectParams || {}, null, 2)})` };
713
+ // ${JSON.stringify(filter || {}, null, 2)}, ${JSON.stringify(selectParams || {}, null, 2)}
714
+ throw { err: parseError(e), msg: `Issue with dbo.${this.name}.find()`, args: { filter, selectParams } };
703
715
  }
704
716
  }
705
717
  findOne(filter, selectParams, param3_unused, table_rules, localParams) {
@@ -888,7 +900,9 @@ class ViewHandler {
888
900
  let table = paths[ji].table;
889
901
  let tableAlias = (0, prostgles_types_1.asName)(ji < paths.length - 1 ? `jd${ji}` : table);
890
902
  let prevTableAlias = (0, prostgles_types_1.asName)(ji ? `jd${ji - 1}` : thisTable);
891
- let cond = `${jp.on.map(([c1, c2]) => `${prevTableAlias}.${(0, prostgles_types_1.asName)(c1)} = ${tableAlias}.${(0, prostgles_types_1.asName)(c2)}`).join("\n AND ")}`;
903
+ let cond = `${jp.on.map(c => {
904
+ return c.map(([c1, c2]) => `${prevTableAlias}.${(0, prostgles_types_1.asName)(c1)} = ${tableAlias}.${(0, prostgles_types_1.asName)(c2)}`).join(" AND ");
905
+ }).join("\n OR ")}`;
892
906
  let j = `SELECT 1 \n` +
893
907
  `FROM ${(0, prostgles_types_1.asName)(table)} ${tableAlias} \n` +
894
908
  `WHERE ${cond} \n`; //
@@ -1416,9 +1430,11 @@ function isPojoObject(obj) {
1416
1430
  }
1417
1431
  return true;
1418
1432
  }
1433
+ exports.isPojoObject = isPojoObject;
1419
1434
  class TableHandler extends ViewHandler {
1420
1435
  constructor(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths) {
1421
1436
  super(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths);
1437
+ this.insertDataParse = insertDataParse_1.insertDataParse;
1422
1438
  this.prepareReturning = async (returning, allowedFields) => {
1423
1439
  let result = [];
1424
1440
  if (returning) {
@@ -1740,321 +1756,9 @@ class TableHandler extends ViewHandler {
1740
1756
  });
1741
1757
  return { data, allowedCols: this.columns.filter(c => dataKeys.includes(c.name)).map(c => c.name) };
1742
1758
  }
1743
- async insertDataParse(data, param2, param3_unused, tableRules, _localParams) {
1744
- const localParams = _localParams || {};
1745
- let dbTX = localParams?.dbTX || this.dbTX;
1746
- const isMultiInsert = Array.isArray(data);
1747
- const getExtraKeys = (d) => Object.keys(d).filter(k => !this.columns.find(c => c.name === k));
1748
- /* Nested insert is not allowed for the file table */
1749
- const isNestedInsert = this.is_media ? false : (Array.isArray(data) ? data : [data]).some(d => getExtraKeys(d).length);
1750
- /**
1751
- * Make sure nested insert uses a transaction
1752
- */
1753
- if (isNestedInsert && !dbTX) {
1754
- return {
1755
- insertResult: await this.dboBuilder.getTX((dbTX) => dbTX[this.name].insert(data, param2, param3_unused, tableRules, { dbTX, ...localParams }))
1756
- };
1757
- }
1758
- // if(!dbTX && this.t) dbTX = this.d;
1759
- const preValidate = tableRules?.insert?.preValidate, validate = tableRules?.insert?.validate;
1760
- let _data = await Promise.all((Array.isArray(data) ? data : [data]).map(async (row) => {
1761
- if (preValidate) {
1762
- row = await preValidate(row);
1763
- }
1764
- const dataKeys = Object.keys(row);
1765
- const extraKeys = getExtraKeys(row);
1766
- /* Upload file then continue insert */
1767
- if (this.is_media) {
1768
- if (!this.dboBuilder.prostgles?.fileManager)
1769
- throw "fileManager not set up";
1770
- const { data, name } = row;
1771
- if (dataKeys.length !== 2)
1772
- throw "Expecting only two properties: { name: string; data: File }";
1773
- // if(!Buffer.isBuffer(data)) throw "data is not of type Buffer"
1774
- if (!data)
1775
- throw "data not provided";
1776
- if (typeof name !== "string") {
1777
- throw "name is not of type string";
1778
- }
1779
- const media_id = (await this.db.oneOrNone("SELECT gen_random_uuid() as name")).name;
1780
- const type = await this.dboBuilder.prostgles.fileManager.getMIME(data, name);
1781
- const media_name = `${media_id}.${type.ext}`;
1782
- let media = {
1783
- id: media_id,
1784
- name: media_name,
1785
- original_name: name,
1786
- extension: type.ext,
1787
- content_type: type.mime
1788
- };
1789
- if (validate) {
1790
- media = await validate(media);
1791
- }
1792
- const _media = await this.dboBuilder.prostgles.fileManager.uploadAsMedia({
1793
- item: {
1794
- data,
1795
- name: media.name ?? "????",
1796
- content_type: media.content_type
1797
- },
1798
- // imageCompression: {
1799
- // inside: {
1800
- // width: 1100,
1801
- // height: 630
1802
- // }
1803
- // }
1804
- });
1805
- return {
1806
- ...media,
1807
- ..._media,
1808
- };
1809
- /* Potentially a nested join */
1810
- }
1811
- else if (extraKeys.length) {
1812
- /* Ensure we're using the same transaction */
1813
- const _this = this.t ? this : dbTX[this.name];
1814
- let rootData = Array.isArray(data) ? data.map(d => (0, PubSubManager_1.omitKeys)(d, extraKeys)) : (0, PubSubManager_1.omitKeys)(data, extraKeys);
1815
- let insertedChildren;
1816
- let targetTableRules;
1817
- const fullRootResult = await _this.insert(rootData, { returning: "*" }, undefined, tableRules, localParams);
1818
- let returnData;
1819
- const returning = param2?.returning;
1820
- if (returning) {
1821
- returnData = {};
1822
- const returningItems = await this.prepareReturning(returning, this.parseFieldFilter(tableRules?.insert?.returningFields));
1823
- returningItems.filter(s => s.selected).map(rs => {
1824
- returnData[rs.alias] = fullRootResult[rs.alias];
1825
- });
1826
- }
1827
- await Promise.all(extraKeys.map(async (targetTable) => {
1828
- const childDataItems = Array.isArray(row[targetTable]) ? row[targetTable] : [row[targetTable]];
1829
- /* Must be allowed to insert into media table */
1830
- const canInsert = async (tbl) => {
1831
- const childRules = await this.dboBuilder.publishParser?.getValidatedRequestRuleWusr({ tableName: tbl, command: "insert", localParams });
1832
- if (!childRules || !childRules.insert)
1833
- throw "Dissallowed nested insert into table " + childRules;
1834
- return childRules;
1835
- };
1836
- // console.log(JSON.stringify(this.dboBuilder.joinPaths, null, 2))
1837
- const jp = this.dboBuilder.joinPaths.find(jp => jp.t1 === this.name && jp.t2 === targetTable);
1838
- if (!jp)
1839
- throw `Could not find a valid table for the nested data { ${targetTable} } `;
1840
- const thisInfo = await this.getInfo();
1841
- const childInsert = async (cdata, tableName) => {
1842
- // console.log("childInsert", {data, tableName})
1843
- if (!cdata || !dbTX?.[tableName] || !("insert" in dbTX[tableName]))
1844
- throw "childInsertErr: Child table handler missing for: " + tableName;
1845
- const tableRules = await canInsert(tableName);
1846
- if (thisInfo.has_media === "one" && thisInfo.media_table_name === tableName && Array.isArray(cdata) && cdata.length > 1) {
1847
- throw "Constraint check fail: Cannot insert more than one record into " + JSON.stringify(tableName);
1848
- }
1849
- return Promise.all((Array.isArray(cdata) ? cdata : [cdata])
1850
- .map(m => dbTX[tableName]
1851
- .insert(m, { returning: "*" }, undefined, tableRules, localParams)
1852
- .catch(e => {
1853
- console.trace({ childInsertErr: e });
1854
- return Promise.reject({ childInsertErr: e });
1855
- })));
1856
- };
1857
- const { path } = jp;
1858
- const [tbl1, tbl2, tbl3] = path;
1859
- targetTableRules = await canInsert(targetTable); // tbl3
1860
- const cols2 = this.dboBuilder.dbo[tbl2].columns || [];
1861
- if (!this.dboBuilder.dbo[tbl2])
1862
- throw "Invalid/disallowed table: " + tbl2;
1863
- const colsRefT1 = cols2?.filter(c => c.references?.cols.length === 1 && c.references?.ftable === tbl1);
1864
- if (!path.length) {
1865
- throw "Nested inserts join path not found for " + [this.name, targetTable];
1866
- }
1867
- else if (path.length === 2) {
1868
- if (targetTable !== tbl2)
1869
- throw "Did not expect this";
1870
- if (!colsRefT1.length)
1871
- throw `Target table ${tbl2} does not reference any columns from the root table ${this.name}. Cannot do nested insert`;
1872
- // console.log(childDataItems, JSON.stringify(colsRefT1, null, 2))
1873
- insertedChildren = await childInsert(childDataItems.map((d) => {
1874
- let result = { ...d };
1875
- colsRefT1.map(col => {
1876
- result[col.references.cols[0]] = fullRootResult[col.references.fcols[0]];
1877
- });
1878
- return result;
1879
- }), targetTable);
1880
- // console.log({ insertedChildren })
1881
- }
1882
- else if (path.length === 3) {
1883
- if (targetTable !== tbl3)
1884
- throw "Did not expect this";
1885
- const colsRefT3 = cols2?.filter(c => c.references?.cols.length === 1 && c.references?.ftable === tbl3);
1886
- if (!colsRefT1.length || !colsRefT3.length)
1887
- throw "Incorrectly referenced or missing columns for nested insert";
1888
- if (targetTable !== this.dboBuilder.prostgles.fileManager?.tableName) {
1889
- throw "Only media allowed to have nested inserts more than 2 tables apart";
1890
- }
1891
- /* We expect tbl2 to have only 2 columns (media_id and foreign_id) */
1892
- if (!cols2 || cols2.find(c => !["media_id", "foreign_id"].includes(c.name))) {
1893
- throw "Second joining table not of expected format";
1894
- }
1895
- insertedChildren = await childInsert(childDataItems, targetTable);
1896
- /* Insert in key_lookup table */
1897
- await Promise.all(insertedChildren.map(async (t3Child) => {
1898
- let tbl2Row = {};
1899
- colsRefT3.map(col => {
1900
- tbl2Row[col.name] = t3Child[col.references.fcols[0]];
1901
- });
1902
- colsRefT1.map(col => {
1903
- tbl2Row[col.name] = fullRootResult[col.references.fcols[0]];
1904
- });
1905
- // console.log({ rootResult, tbl2Row, t3Child, colsRefT3, colsRefT1, t: this.t?.ctx?.start });
1906
- await childInsert(tbl2Row, tbl2); //.then(() => {});
1907
- }));
1908
- }
1909
- else {
1910
- console.error(JSON.stringify({ path, thisTable: this.name, targetTable }, null, 2));
1911
- throw "Unexpected path for Nested inserts";
1912
- }
1913
- /* Return also the nested inserted data */
1914
- if (targetTableRules && insertedChildren?.length && returning) {
1915
- const targetTableHandler = dbTX[targetTable];
1916
- const targetReturning = await targetTableHandler.prepareReturning("*", targetTableHandler.parseFieldFilter(targetTableRules?.insert?.returningFields));
1917
- let clientTargetInserts = insertedChildren.map(d => {
1918
- let _d = { ...d };
1919
- let res = {};
1920
- targetReturning.map(r => {
1921
- res[r.alias] = _d[r.alias];
1922
- });
1923
- return res;
1924
- });
1925
- returnData[targetTable] = clientTargetInserts.length === 1 ? clientTargetInserts[0] : clientTargetInserts;
1926
- }
1927
- }));
1928
- return returnData;
1929
- }
1930
- return row;
1931
- }));
1932
- let result = isMultiInsert ? _data : _data[0];
1933
- // if(validate && !isNestedInsert){
1934
- // result = isMultiInsert? await Promise.all(_data.map(async d => await validate({ ...d }))) : await validate({ ..._data[0] });
1935
- // }
1936
- let res = isNestedInsert ?
1937
- { insertResult: result } :
1938
- { data: result };
1939
- return res;
1940
- }
1941
1759
  async insert(rowOrRows, param2, param3_unused, tableRules, _localParams) {
1942
- const localParams = _localParams || {};
1943
- const { dbTX } = localParams;
1944
- try {
1945
- const { returning, onConflictDoNothing, fixIssues = false } = param2 || {};
1946
- const { testRule = false, returnQuery = false } = localParams || {};
1947
- let returningFields, forcedData, fields;
1948
- if (tableRules) {
1949
- if (!tableRules.insert)
1950
- throw "insert rules missing for " + this.name;
1951
- returningFields = tableRules.insert.returningFields;
1952
- forcedData = tableRules.insert.forcedData;
1953
- fields = tableRules.insert.fields;
1954
- /* If no returning fields specified then take select fields as returning */
1955
- if (!returningFields)
1956
- returningFields = (0, utils_1.get)(tableRules, "select.fields") || (0, utils_1.get)(tableRules, "insert.fields");
1957
- if (!fields)
1958
- throw ` invalid insert rule for ${this.name} -> fields missing `;
1959
- /* Safely test publish rules */
1960
- if (testRule) {
1961
- // if(this.is_media && tableRules.insert.preValidate) throw "Media table cannot have a preValidate. It already is used internally by prostgles for file upload";
1962
- await this.validateViewRules({ fields, returningFields, forcedFilter: forcedData, rule: "insert" });
1963
- if (forcedData) {
1964
- const keys = Object.keys(forcedData);
1965
- if (keys.length) {
1966
- try {
1967
- 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);
1968
- await this.db.any("EXPLAIN INSERT INTO " + this.escapedName + " (${colNames:raw}) SELECT * FROM ( VALUES ${values:raw} ) t WHERE FALSE;", { colNames, values });
1969
- }
1970
- catch (e) {
1971
- throw "\nissue with forcedData: \nVALUE: " + JSON.stringify(forcedData, null, 2) + "\nERROR: " + e;
1972
- }
1973
- }
1974
- }
1975
- return true;
1976
- }
1977
- }
1978
- let conflict_query = "";
1979
- if (typeof onConflictDoNothing === "boolean" && onConflictDoNothing) {
1980
- conflict_query = " ON CONFLICT DO NOTHING ";
1981
- }
1982
- if (param2) {
1983
- const good_params = ["returning", "multi", "onConflictDoNothing", "fixIssues"];
1984
- const bad_params = Object.keys(param2).filter(k => !good_params.includes(k));
1985
- if (bad_params && bad_params.length)
1986
- throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", ");
1987
- }
1988
- if (!rowOrRows)
1989
- rowOrRows = {}; //throw "Provide data in param1";
1990
- let returningSelect = this.makeReturnQuery(await this.prepareReturning(returning, this.parseFieldFilter(returningFields)));
1991
- const makeQuery = async (_row, isOne = false) => {
1992
- let row = { ..._row };
1993
- if (!isPojoObject(row)) {
1994
- console.trace(row);
1995
- throw "\ninvalid insert data provided -> " + JSON.stringify(row);
1996
- }
1997
- const { data, allowedCols } = this.validateNewData({ row, forcedData, allowedFields: fields, tableRules, fixIssues });
1998
- let _data = { ...data };
1999
- let insertQ = "";
2000
- if (!Object.keys(_data).length)
2001
- insertQ = `INSERT INTO ${(0, prostgles_types_1.asName)(this.name)} DEFAULT VALUES `;
2002
- else
2003
- insertQ = await this.colSet.getInsertQuery(_data, allowedCols, tableRules?.insert?.validate); // pgp.helpers.insert(_data, columnSet);
2004
- return insertQ + conflict_query + returningSelect;
2005
- };
2006
- let query = "";
2007
- let queryType = "none";
2008
- /**
2009
- * If media it will: upload file and continue insert
2010
- * If nested insert it will: make separate inserts and not continue main insert
2011
- */
2012
- const insRes = await this.insertDataParse(rowOrRows, param2, param3_unused, tableRules, localParams);
2013
- const { data, insertResult } = insRes;
2014
- if ("insertResult" in insRes) {
2015
- return insertResult;
2016
- }
2017
- if (Array.isArray(data)) {
2018
- // if(returning) throw "Sorry but [returning] is dissalowed for multi insert";
2019
- let queries = await Promise.all(data.map(async (p) => {
2020
- const q = await makeQuery(p);
2021
- return q;
2022
- }));
2023
- query = exports.pgp.helpers.concat(queries);
2024
- if (returning)
2025
- queryType = "many";
2026
- }
2027
- else {
2028
- query = await makeQuery(data, true);
2029
- if (returning)
2030
- queryType = "one";
2031
- }
2032
- if (returnQuery)
2033
- return query;
2034
- let result;
2035
- if (this.dboBuilder.prostgles.opts.DEBUG_MODE) {
2036
- console.log(this.t?.ctx?.start, "insert in " + this.name, data);
2037
- }
2038
- const tx = dbTX?.[this.name]?.t || this.t;
2039
- const allowedFieldKeys = this.parseFieldFilter(fields);
2040
- if (tx) {
2041
- result = tx[queryType](query).catch((err) => makeErr(err, localParams, this, allowedFieldKeys));
2042
- }
2043
- else {
2044
- result = this.db.tx(t => t[queryType](query)).catch(err => makeErr(err, localParams, this, allowedFieldKeys));
2045
- }
2046
- return result;
2047
- }
2048
- catch (e) {
2049
- if (localParams && localParams.testRule)
2050
- throw e;
2051
- throw { err: parseError(e), msg: `Issue with dbo.${this.name}.insert(
2052
- ${JSON.stringify(rowOrRows || {}, null, 2)},
2053
- ${JSON.stringify(param2 || {}, null, 2)}
2054
- )` };
2055
- }
1760
+ return insert_1.insert.bind(this)(rowOrRows, param2, param3_unused, tableRules, _localParams);
2056
1761
  }
2057
- ;
2058
1762
  makeReturnQuery(items) {
2059
1763
  if (items?.length)
2060
1764
  return " RETURNING " + items.map(s => s.getQuery() + " AS " + (0, prostgles_types_1.asName)(s.alias)).join(", ");
@@ -2252,9 +1956,9 @@ class DboBuilder {
2252
1956
  this.getPubSubManager = async () => {
2253
1957
  if (!this._pubSubManager) {
2254
1958
  let onSchemaChange;
2255
- if (this.prostgles.opts.watchSchema && this.prostgles.opts.watchSchemaType === "events") {
1959
+ if (this.prostgles.opts.watchSchema && this.prostgles.opts.watchSchemaType === "DDL_trigger") {
2256
1960
  if (!this.prostgles.isSuperUser) {
2257
- console.warn(`watchSchemaType "events" cannot be used because db user is not a superuser. Will fallback to watchSchemaType "queries" `);
1961
+ console.warn(`watchSchemaType "events" cannot be used because db user is not a superuser. Will fallback to watchSchemaType "prostgles_queries" `);
2258
1962
  }
2259
1963
  else {
2260
1964
  onSchemaChange = (event) => {
@@ -2274,15 +1978,17 @@ class DboBuilder {
2274
1978
  console.warn(`subscribe and sync cannot be used because db user is not a superuser `);
2275
1979
  }
2276
1980
  }
2277
- if (!this._pubSubManager)
1981
+ if (!this._pubSubManager) {
1982
+ console.trace("Could not create this._pubSubManager");
2278
1983
  throw "Could not create this._pubSubManager";
1984
+ }
2279
1985
  return this._pubSubManager;
2280
1986
  };
2281
1987
  this.joinPaths = [];
2282
1988
  this.init = async () => {
2283
1989
  /* If watchSchema then PubSubManager must be created */
2284
1990
  await this.build();
2285
- if (this.prostgles.opts.watchSchema) {
1991
+ if (this.prostgles.opts.watchSchema && (this.prostgles.opts.watchSchemaType === "DDL_trigger" || !this.prostgles.opts.watchSchemaType) && this.prostgles.isSuperUser) {
2286
1992
  await this.getPubSubManager();
2287
1993
  }
2288
1994
  return this;
@@ -2330,8 +2036,10 @@ class DboBuilder {
2330
2036
  async parseJoins() {
2331
2037
  if (this.prostgles.opts.joins) {
2332
2038
  let _joins = await this.prostgles.opts.joins;
2333
- let inferredJoins = await getInferredJoins(this.db, this.prostgles.opts.schema);
2334
- if (typeof _joins === "string" && _joins === "inferred") {
2039
+ if (!this.tablesOrViews)
2040
+ throw new Error("Could not create join config. this.tablesOrViews missing");
2041
+ let inferredJoins = await getInferredJoins2(this.tablesOrViews);
2042
+ if (_joins === "inferred") {
2335
2043
  _joins = inferredJoins;
2336
2044
  /* If joins are specified then include inferred joins except the explicit tables */
2337
2045
  }
@@ -2339,6 +2047,9 @@ class DboBuilder {
2339
2047
  const joinTables = _joins.map(j => j.tables).flat();
2340
2048
  _joins = _joins.concat(inferredJoins.filter(j => !j.tables.find(t => joinTables.includes(t))));
2341
2049
  }
2050
+ else if (_joins) {
2051
+ throw new Error("Unexpected joins init param. Expecting 'inferred' OR joinConfig but got: " + JSON.stringify(_joins));
2052
+ }
2342
2053
  let joins = JSON.parse(JSON.stringify(_joins));
2343
2054
  this.joins = joins;
2344
2055
  // Validate joins
@@ -2541,11 +2252,16 @@ class DboBuilder {
2541
2252
  */
2542
2253
  const { watchSchema, watchSchemaType } = this.prostgles?.opts || {};
2543
2254
  if (watchSchema &&
2544
- (!this.prostgles.isSuperUser || watchSchemaType === "queries") &&
2545
- (["CREATE", "ALTER", "DROP"].includes(command) ||
2546
- // Cover this case: `CREATE TABLE mytable AS SELECT`
2547
- query && query.toLowerCase().replace(/\s\s+/g, ' ').includes("create table"))) {
2548
- this.prostgles.onSchemaChange({ command, query });
2255
+ (!this.prostgles.isSuperUser || watchSchemaType === "prostgles_queries")) {
2256
+ if (["CREATE", "ALTER", "DROP"].includes(command)) {
2257
+ this.prostgles.onSchemaChange({ command, query });
2258
+ }
2259
+ else if (query) {
2260
+ const cleanedQuery = query.toLowerCase().replace(/\s\s+/g, ' ');
2261
+ if (PubSubManager_1.PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase()))) {
2262
+ this.prostgles.onSchemaChange({ command, query });
2263
+ }
2264
+ }
2549
2265
  }
2550
2266
  if (command === "LISTEN") {
2551
2267
  if (!allowListen)
@@ -3035,50 +2751,81 @@ function sqlErrCodeToMsg(code) {
3035
2751
  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] }), {}))
3036
2752
  */
3037
2753
  }
3038
- async function getInferredJoins(db, schema = "public") {
2754
+ async function getInferredJoins2(schema) {
3039
2755
  let joins = [];
3040
- let res = await db.any(`SELECT
3041
- tc.table_schema,
3042
- tc.constraint_name,
3043
- tc.table_name,
3044
- kcu.column_name,
3045
- ccu.table_schema AS foreign_table_schema,
3046
- ccu.table_name AS foreign_table_name,
3047
- ccu.column_name AS foreign_column_name,
3048
- tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') as foreign_is_unique
3049
- FROM
3050
- information_schema.table_constraints AS tc
3051
- JOIN information_schema.key_column_usage AS kcu
3052
- ON tc.constraint_name = kcu.constraint_name
3053
- AND tc.table_schema = kcu.table_schema
3054
- JOIN information_schema.constraint_column_usage AS ccu
3055
- ON ccu.constraint_name = tc.constraint_name
3056
- AND ccu.table_schema = tc.table_schema
3057
- WHERE tc.table_schema=` + "${schema}" + `
3058
- AND tc.constraint_type = 'FOREIGN KEY'
3059
- AND tc.table_name <> ccu.table_name -- Exclude self-referencing tables
3060
- `, { schema });
3061
- res.map((d) => {
3062
- let eIdx = joins.findIndex(j => j.tables.includes(d.table_name) && j.tables.includes(d.foreign_table_name));
3063
- let existing = joins[eIdx];
2756
+ const upsertJoin = (t1, t2, cols) => {
2757
+ let existingIdx = joins.findIndex(j => j.tables.slice(0).sort().join() === [t1, t2].sort().join());
2758
+ let existing = joins[existingIdx];
2759
+ const normalCond = cols.reduce((a, v) => ({ ...a, [v.col1]: v.col2 }), {});
2760
+ const revertedCond = cols.reduce((a, v) => ({ ...a, [v.col2]: v.col1 }), {});
3064
2761
  if (existing) {
3065
- if (existing.tables[0] === d.table_name) {
3066
- existing.on = { ...existing.on, [d.column_name]: d.foreign_column_name };
3067
- }
3068
- else {
3069
- existing.on = { ...existing.on, [d.foreign_column_name]: d.column_name };
2762
+ const cond = existing.tables[0] === t1 ? normalCond : revertedCond;
2763
+ /** Avoid duplicates */
2764
+ if (!existing.on.some(_cond => JSON.stringify(_cond) === JSON.stringify(cond))) {
2765
+ existing.on.push(cond);
2766
+ joins[existingIdx] = existing;
3070
2767
  }
3071
- joins[eIdx] = existing;
3072
2768
  }
3073
2769
  else {
3074
2770
  joins.push({
3075
- tables: [d.table_name, d.foreign_table_name],
3076
- on: {
3077
- [d.column_name]: d.foreign_column_name
3078
- },
2771
+ tables: [t1, t2],
2772
+ on: [normalCond],
3079
2773
  type: "many-many"
3080
2774
  });
3081
2775
  }
2776
+ };
2777
+ schema.map(tov => {
2778
+ tov.columns.map(col => {
2779
+ if (col.references) {
2780
+ const r = col.references;
2781
+ upsertJoin(tov.name, r.ftable, r.cols.map((c, i) => ({ col1: c, col2: r.fcols[i] })));
2782
+ }
2783
+ });
3082
2784
  });
3083
2785
  return joins;
3084
2786
  }
2787
+ // async function getInferredJoins(db: DB, schema: string = "public"): Promise<Join[]>{
2788
+ // let joins: Join[] = [];
2789
+ // let res = await db.any(`SELECT
2790
+ // tc.table_schema,
2791
+ // tc.constraint_name,
2792
+ // tc.table_name,
2793
+ // kcu.column_name,
2794
+ // ccu.table_schema AS foreign_table_schema,
2795
+ // ccu.table_name AS foreign_table_name,
2796
+ // ccu.column_name AS foreign_column_name,
2797
+ // tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') as foreign_is_unique
2798
+ // FROM
2799
+ // information_schema.table_constraints AS tc
2800
+ // JOIN information_schema.key_column_usage AS kcu
2801
+ // ON tc.constraint_name = kcu.constraint_name
2802
+ // AND tc.table_schema = kcu.table_schema
2803
+ // JOIN information_schema.constraint_column_usage AS ccu
2804
+ // ON ccu.constraint_name = tc.constraint_name
2805
+ // AND ccu.table_schema = tc.table_schema
2806
+ // WHERE tc.table_schema=` + "${schema}" + `
2807
+ // AND tc.constraint_type = 'FOREIGN KEY'
2808
+ // AND tc.table_name <> ccu.table_name -- Exclude self-referencing tables
2809
+ // `, { schema });
2810
+ // res.map((d: any) => {
2811
+ // let eIdx = joins.findIndex(j => j.tables.includes(d.table_name) && j.tables.includes(d.foreign_table_name));
2812
+ // let existing = joins[eIdx];
2813
+ // if(existing){
2814
+ // if(existing.tables[0] === d.table_name){
2815
+ // existing.on = { ...existing.on, [d.column_name]: d.foreign_column_name }
2816
+ // } else {
2817
+ // existing.on = { ...existing.on, [d.foreign_column_name]: d.column_name }
2818
+ // }
2819
+ // joins[eIdx] = existing;
2820
+ // } else {
2821
+ // joins.push({
2822
+ // tables: [d.table_name, d.foreign_table_name],
2823
+ // on: {
2824
+ // [d.column_name]: d.foreign_column_name
2825
+ // },
2826
+ // type: "many-many"
2827
+ // })
2828
+ // }
2829
+ // });
2830
+ // return joins;
2831
+ // }