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/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 ${
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
on
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 === "
|
|
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 "
|
|
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
|
-
|
|
2334
|
-
|
|
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 === "
|
|
2545
|
-
(["CREATE", "ALTER", "DROP"].includes(command)
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
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
|
|
2754
|
+
async function getInferredJoins2(schema) {
|
|
3039
2755
|
let joins = [];
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
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
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
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: [
|
|
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
|
+
// }
|