prostgles-server 2.0.187 → 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 (75) 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 +105 -365
  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 +1 -1
  17. package/dist/Prostgles.d.ts.map +1 -1
  18. package/dist/Prostgles.js.map +1 -1
  19. package/dist/QueryBuilder.d.ts.map +1 -1
  20. package/dist/QueryBuilder.js +7 -2
  21. package/dist/QueryBuilder.js.map +1 -1
  22. package/dist/TableConfig.d.ts +1 -4
  23. package/dist/TableConfig.d.ts.map +1 -1
  24. package/dist/TableConfig.js.map +1 -1
  25. package/lib/DboBuilder/insert.d.ts +5 -0
  26. package/lib/DboBuilder/insert.d.ts.map +1 -0
  27. package/lib/DboBuilder/insert.js +128 -0
  28. package/lib/DboBuilder/insert.ts +138 -0
  29. package/lib/DboBuilder/insertDataParse.d.ts +11 -0
  30. package/lib/DboBuilder/insertDataParse.d.ts.map +1 -0
  31. package/lib/DboBuilder/insertDataParse.js +282 -0
  32. package/lib/DboBuilder/insertDataParse.ts +355 -0
  33. package/lib/DboBuilder.d.ts +10 -5
  34. package/lib/DboBuilder.d.ts.map +1 -1
  35. package/lib/DboBuilder.js +105 -365
  36. package/lib/DboBuilder.ts +121 -440
  37. package/lib/FileManager.d.ts.map +1 -1
  38. package/lib/FileManager.js +17 -12
  39. package/lib/FileManager.ts +18 -13
  40. package/lib/Prostgles.d.ts +1 -1
  41. package/lib/Prostgles.d.ts.map +1 -1
  42. package/lib/Prostgles.ts +664 -652
  43. package/lib/QueryBuilder.d.ts.map +1 -1
  44. package/lib/QueryBuilder.js +7 -2
  45. package/lib/QueryBuilder.ts +12 -7
  46. package/lib/TableConfig.d.ts +1 -4
  47. package/lib/TableConfig.d.ts.map +1 -1
  48. package/lib/TableConfig.ts +2 -6
  49. package/package.json +2 -2
  50. package/tests/client/PID.txt +1 -1
  51. package/tests/client/tsconfig.json +1 -1
  52. package/tests/client_only_queries.d.ts +1 -1
  53. package/tests/client_only_queries.d.ts.map +1 -1
  54. package/tests/client_only_queries.ts +1 -1
  55. package/tests/isomorphic_queries.d.ts +1 -1
  56. package/tests/isomorphic_queries.d.ts.map +1 -1
  57. package/tests/isomorphic_queries.js +48 -1
  58. package/tests/isomorphic_queries.ts +65 -4
  59. package/tests/manual_test/DBoGenerated.d.ts +398 -0
  60. package/tests/manual_test/index.d.ts +2 -0
  61. package/tests/manual_test/index.d.ts.map +1 -0
  62. package/tests/{config_test2 → manual_test}/index.html +14 -23
  63. package/tests/{config_test2 → manual_test}/index.js +21 -15
  64. package/tests/{config_test2 → manual_test}/index.ts +22 -17
  65. package/tests/{config_test2 → manual_test}/init.sql +36 -5
  66. package/tests/manual_test/package-lock.json +2483 -0
  67. package/tests/{config_test2 → manual_test}/package.json +6 -7
  68. package/tests/manual_test/tsconfig.json +21 -0
  69. package/tests/server/DBoGenerated.d.ts +70 -0
  70. package/tests/server/index.js +29 -2
  71. package/tests/server/index.ts +30 -4
  72. package/tests/server/init.sql +25 -0
  73. package/tests/server/package-lock.json +3 -3
  74. package/tests/config_test2/DBoGenerated.d.ts +0 -135
  75. 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(", ");
@@ -2332,8 +2036,10 @@ class DboBuilder {
2332
2036
  async parseJoins() {
2333
2037
  if (this.prostgles.opts.joins) {
2334
2038
  let _joins = await this.prostgles.opts.joins;
2335
- let inferredJoins = await getInferredJoins(this.db, this.prostgles.opts.schema);
2336
- 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") {
2337
2043
  _joins = inferredJoins;
2338
2044
  /* If joins are specified then include inferred joins except the explicit tables */
2339
2045
  }
@@ -2341,6 +2047,9 @@ class DboBuilder {
2341
2047
  const joinTables = _joins.map(j => j.tables).flat();
2342
2048
  _joins = _joins.concat(inferredJoins.filter(j => !j.tables.find(t => joinTables.includes(t))));
2343
2049
  }
2050
+ else if (_joins) {
2051
+ throw new Error("Unexpected joins init param. Expecting 'inferred' OR joinConfig but got: " + JSON.stringify(_joins));
2052
+ }
2344
2053
  let joins = JSON.parse(JSON.stringify(_joins));
2345
2054
  this.joins = joins;
2346
2055
  // Validate joins
@@ -3042,50 +2751,81 @@ function sqlErrCodeToMsg(code) {
3042
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] }), {}))
3043
2752
  */
3044
2753
  }
3045
- async function getInferredJoins(db, schema = "public") {
2754
+ async function getInferredJoins2(schema) {
3046
2755
  let joins = [];
3047
- let res = await db.any(`SELECT
3048
- tc.table_schema,
3049
- tc.constraint_name,
3050
- tc.table_name,
3051
- kcu.column_name,
3052
- ccu.table_schema AS foreign_table_schema,
3053
- ccu.table_name AS foreign_table_name,
3054
- ccu.column_name AS foreign_column_name,
3055
- tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') as foreign_is_unique
3056
- FROM
3057
- information_schema.table_constraints AS tc
3058
- JOIN information_schema.key_column_usage AS kcu
3059
- ON tc.constraint_name = kcu.constraint_name
3060
- AND tc.table_schema = kcu.table_schema
3061
- JOIN information_schema.constraint_column_usage AS ccu
3062
- ON ccu.constraint_name = tc.constraint_name
3063
- AND ccu.table_schema = tc.table_schema
3064
- WHERE tc.table_schema=` + "${schema}" + `
3065
- AND tc.constraint_type = 'FOREIGN KEY'
3066
- AND tc.table_name <> ccu.table_name -- Exclude self-referencing tables
3067
- `, { schema });
3068
- res.map((d) => {
3069
- let eIdx = joins.findIndex(j => j.tables.includes(d.table_name) && j.tables.includes(d.foreign_table_name));
3070
- 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 }), {});
3071
2761
  if (existing) {
3072
- if (existing.tables[0] === d.table_name) {
3073
- existing.on = { ...existing.on, [d.column_name]: d.foreign_column_name };
3074
- }
3075
- else {
3076
- 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;
3077
2767
  }
3078
- joins[eIdx] = existing;
3079
2768
  }
3080
2769
  else {
3081
2770
  joins.push({
3082
- tables: [d.table_name, d.foreign_table_name],
3083
- on: {
3084
- [d.column_name]: d.foreign_column_name
3085
- },
2771
+ tables: [t1, t2],
2772
+ on: [normalCond],
3086
2773
  type: "many-many"
3087
2774
  });
3088
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
+ });
3089
2784
  });
3090
2785
  return joins;
3091
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
+ // }