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.
- package/dist/DboBuilder/insert.d.ts +5 -0
- package/dist/DboBuilder/insert.d.ts.map +1 -0
- package/dist/DboBuilder/insert.js +129 -0
- package/dist/DboBuilder/insert.js.map +1 -0
- package/dist/DboBuilder/insertDataParse.d.ts +11 -0
- package/dist/DboBuilder/insertDataParse.d.ts.map +1 -0
- package/dist/DboBuilder/insertDataParse.js +283 -0
- package/dist/DboBuilder/insertDataParse.js.map +1 -0
- package/dist/DboBuilder.d.ts +10 -5
- package/dist/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder.js +121 -374
- package/dist/DboBuilder.js.map +1 -1
- package/dist/FileManager.d.ts.map +1 -1
- package/dist/FileManager.js +17 -12
- package/dist/FileManager.js.map +1 -1
- package/dist/Prostgles.d.ts +16 -14
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +7 -7
- package/dist/Prostgles.js.map +1 -1
- package/dist/PubSubManager.d.ts +1 -0
- package/dist/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager.js +2 -1
- package/dist/PubSubManager.js.map +1 -1
- package/dist/QueryBuilder.d.ts +7 -3
- package/dist/QueryBuilder.d.ts.map +1 -1
- package/dist/QueryBuilder.js +7 -2
- package/dist/QueryBuilder.js.map +1 -1
- package/dist/TableConfig.d.ts +1 -4
- package/dist/TableConfig.d.ts.map +1 -1
- package/dist/TableConfig.js +16 -1
- package/dist/TableConfig.js.map +1 -1
- package/lib/DboBuilder/insert.d.ts +5 -0
- package/lib/DboBuilder/insert.d.ts.map +1 -0
- package/lib/DboBuilder/insert.js +128 -0
- package/lib/DboBuilder/insert.ts +138 -0
- package/lib/DboBuilder/insertDataParse.d.ts +11 -0
- package/lib/DboBuilder/insertDataParse.d.ts.map +1 -0
- package/lib/DboBuilder/insertDataParse.js +282 -0
- package/lib/DboBuilder/insertDataParse.ts +355 -0
- package/lib/DboBuilder.d.ts +10 -5
- package/lib/DboBuilder.d.ts.map +1 -1
- package/lib/DboBuilder.js +121 -374
- package/lib/DboBuilder.ts +138 -453
- package/lib/FileManager.d.ts.map +1 -1
- package/lib/FileManager.js +17 -12
- package/lib/FileManager.ts +18 -13
- package/lib/Prostgles.d.ts +16 -14
- package/lib/Prostgles.d.ts.map +1 -1
- package/lib/Prostgles.js +7 -7
- package/lib/Prostgles.ts +664 -650
- package/lib/PubSubManager.d.ts +1 -0
- package/lib/PubSubManager.d.ts.map +1 -1
- package/lib/PubSubManager.js +2 -1
- package/lib/PubSubManager.ts +3 -1
- package/lib/QueryBuilder.d.ts.map +1 -1
- package/lib/QueryBuilder.js +7 -2
- package/lib/QueryBuilder.ts +12 -7
- package/lib/SchemaWatchManager.ts +72 -0
- package/lib/TableConfig.d.ts +1 -4
- package/lib/TableConfig.d.ts.map +1 -1
- package/lib/TableConfig.js +16 -1
- package/lib/TableConfig.ts +21 -8
- package/package.json +3 -3
- package/tests/client/PID.txt +1 -1
- package/tests/client/package-lock.json +15 -15
- package/tests/client/package.json +1 -1
- package/tests/client/tsconfig.json +1 -1
- package/tests/client_only_queries.d.ts +1 -1
- package/tests/client_only_queries.d.ts.map +1 -1
- package/tests/client_only_queries.ts +1 -1
- package/tests/isomorphic_queries.d.ts +1 -1
- package/tests/isomorphic_queries.d.ts.map +1 -1
- package/tests/isomorphic_queries.js +49 -1
- package/tests/isomorphic_queries.ts +66 -4
- package/tests/manual_test/DBoGenerated.d.ts +398 -0
- package/tests/manual_test/index.d.ts +2 -0
- package/tests/manual_test/index.d.ts.map +1 -0
- package/tests/{config_test2 → manual_test}/index.html +14 -23
- package/tests/{config_test2 → manual_test}/index.js +21 -15
- package/tests/{config_test2 → manual_test}/index.ts +22 -17
- package/tests/{config_test2 → manual_test}/init.sql +36 -5
- package/tests/manual_test/package-lock.json +2483 -0
- package/tests/{config_test2 → manual_test}/package.json +6 -7
- package/tests/manual_test/tsconfig.json +21 -0
- package/tests/server/DBoGenerated.d.ts +70 -0
- package/tests/server/index.js +29 -2
- package/tests/server/index.ts +30 -4
- package/tests/server/init.sql +25 -0
- package/tests/server/package-lock.json +5 -5
- package/tests/config_test2/DBoGenerated.d.ts +0 -135
- package/tests/config_test2/tsconfig.json +0 -21
package/dist/DboBuilder.js
CHANGED
|
@@ -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 ${
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
on
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 === "
|
|
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 "
|
|
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
|
-
|
|
2311
|
-
|
|
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 === "
|
|
2522
|
-
(["CREATE", "ALTER", "DROP"].includes(command)
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
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
|
|
2731
|
+
async function getInferredJoins2(schema) {
|
|
3016
2732
|
let joins = [];
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
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
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
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: [
|
|
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
|