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
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import pgPromise from "pg-promise";
|
|
2
|
+
import { AnyObject, asName, FieldFilter, get, InsertParams, isObject } from "prostgles-types";
|
|
3
|
+
import { isPojoObject, LocalParams, makeErr, parseError, pgp, TableHandler } from "../DboBuilder";
|
|
4
|
+
import { TableRule } from "../PublishParser";
|
|
5
|
+
|
|
6
|
+
export async function insert(this: TableHandler, rowOrRows: (AnyObject | AnyObject[]), param2?: InsertParams, param3_unused?: undefined, tableRules?: TableRule, _localParams?: LocalParams): Promise<any | any[] | boolean> {
|
|
7
|
+
const localParams = _localParams || {};
|
|
8
|
+
const { dbTX } = localParams
|
|
9
|
+
try {
|
|
10
|
+
|
|
11
|
+
const { returning, onConflictDoNothing, fixIssues = false } = param2 || {};
|
|
12
|
+
const { testRule = false, returnQuery = false } = localParams || {};
|
|
13
|
+
|
|
14
|
+
let returningFields: FieldFilter | undefined,
|
|
15
|
+
forcedData: AnyObject | undefined,
|
|
16
|
+
fields: FieldFilter | undefined;
|
|
17
|
+
|
|
18
|
+
if (tableRules) {
|
|
19
|
+
if (!tableRules.insert) throw "insert rules missing for " + this.name;
|
|
20
|
+
returningFields = tableRules.insert.returningFields;
|
|
21
|
+
forcedData = tableRules.insert.forcedData;
|
|
22
|
+
fields = tableRules.insert.fields;
|
|
23
|
+
|
|
24
|
+
/* If no returning fields specified then take select fields as returning */
|
|
25
|
+
if (!returningFields) returningFields = get(tableRules, "select.fields") || get(tableRules, "insert.fields");
|
|
26
|
+
|
|
27
|
+
if (!fields) throw ` invalid insert rule for ${this.name} -> fields missing `;
|
|
28
|
+
|
|
29
|
+
/* Safely test publish rules */
|
|
30
|
+
if (testRule) {
|
|
31
|
+
// if(this.is_media && tableRules.insert.preValidate) throw "Media table cannot have a preValidate. It already is used internally by prostgles for file upload";
|
|
32
|
+
await this.validateViewRules({ fields, returningFields, forcedFilter: forcedData, rule: "insert" });
|
|
33
|
+
if (forcedData) {
|
|
34
|
+
const keys = Object.keys(forcedData);
|
|
35
|
+
if (keys.length) {
|
|
36
|
+
try {
|
|
37
|
+
const colset = new 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 }))),
|
|
38
|
+
values = pgp.helpers.values(forcedData, colset),
|
|
39
|
+
colNames = this.prepareSelect(keys, this.column_names);
|
|
40
|
+
await this.db.any("EXPLAIN INSERT INTO " + this.escapedName + " (${colNames:raw}) SELECT * FROM ( VALUES ${values:raw} ) t WHERE FALSE;", { colNames, values })
|
|
41
|
+
} catch (e) {
|
|
42
|
+
throw "\nissue with forcedData: \nVALUE: " + JSON.stringify(forcedData, null, 2) + "\nERROR: " + e;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let conflict_query = "";
|
|
52
|
+
if (typeof onConflictDoNothing === "boolean" && onConflictDoNothing) {
|
|
53
|
+
conflict_query = " ON CONFLICT DO NOTHING ";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (param2) {
|
|
57
|
+
const good_params = ["returning", "multi", "onConflictDoNothing", "fixIssues"];
|
|
58
|
+
const bad_params = Object.keys(param2).filter(k => !good_params.includes(k));
|
|
59
|
+
if (bad_params && bad_params.length) throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", ");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!rowOrRows) rowOrRows = {}; //throw "Provide data in param1";
|
|
63
|
+
let returningSelect = this.makeReturnQuery(await this.prepareReturning(returning, this.parseFieldFilter(returningFields)));
|
|
64
|
+
const makeQuery = async (_row: AnyObject | undefined, isOne = false) => {
|
|
65
|
+
let row = { ..._row };
|
|
66
|
+
|
|
67
|
+
if (!isPojoObject(row)) {
|
|
68
|
+
console.trace(row)
|
|
69
|
+
throw "\ninvalid insert data provided -> " + JSON.stringify(row);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { data, allowedCols } = this.validateNewData({ row, forcedData, allowedFields: fields, tableRules, fixIssues });
|
|
73
|
+
let _data = { ...data };
|
|
74
|
+
|
|
75
|
+
let insertQ = "";
|
|
76
|
+
if (!Object.keys(_data).length) insertQ = `INSERT INTO ${asName(this.name)} DEFAULT VALUES `;
|
|
77
|
+
else insertQ = await this.colSet.getInsertQuery(_data, allowedCols, tableRules?.insert?.validate) // pgp.helpers.insert(_data, columnSet);
|
|
78
|
+
return insertQ + conflict_query + returningSelect;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
let query = "";
|
|
82
|
+
let queryType: keyof pgPromise.ITask<{}> = "none";
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* If media it will: upload file and continue insert
|
|
86
|
+
* If nested insert it will: make separate inserts and not continue main insert
|
|
87
|
+
*/
|
|
88
|
+
const insRes = await this.insertDataParse(rowOrRows, param2, param3_unused, tableRules, localParams);
|
|
89
|
+
const { data, insertResult } = insRes;
|
|
90
|
+
if ("insertResult" in insRes) {
|
|
91
|
+
return insertResult;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(data)) {
|
|
95
|
+
// if(returning) throw "Sorry but [returning] is dissalowed for multi insert";
|
|
96
|
+
let queries = await Promise.all(data.map(async p => {
|
|
97
|
+
const q = await makeQuery(p);
|
|
98
|
+
return q;
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
query = pgp.helpers.concat(queries);
|
|
102
|
+
if (returning) queryType = "many";
|
|
103
|
+
} else {
|
|
104
|
+
query = await makeQuery(data, true);
|
|
105
|
+
if (returning) queryType = "one";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (returnQuery) return query;
|
|
109
|
+
let result;
|
|
110
|
+
|
|
111
|
+
if (this.dboBuilder.prostgles.opts.DEBUG_MODE) {
|
|
112
|
+
console.log(this.t?.ctx?.start, "insert in " + this.name, data);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const tx = dbTX?.[this.name]?.t || this.t;
|
|
116
|
+
|
|
117
|
+
const allowedFieldKeys = this.parseFieldFilter(fields);
|
|
118
|
+
if (tx) {
|
|
119
|
+
result = (tx as any)[queryType](query).catch((err: any) => makeErr(err, localParams, this, allowedFieldKeys));
|
|
120
|
+
} else {
|
|
121
|
+
result = this.db.tx(t => (t as any)[queryType](query)).catch(err => makeErr(err, localParams, this, allowedFieldKeys));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result;
|
|
125
|
+
} catch (e) {
|
|
126
|
+
if (localParams && localParams.testRule) throw e;
|
|
127
|
+
|
|
128
|
+
// ${JSON.stringify(rowOrRows || {}, null, 2)},
|
|
129
|
+
// ${JSON.stringify(param2 || {}, null, 2)}
|
|
130
|
+
throw {
|
|
131
|
+
err: parseError(e), msg: `Issue with dbo.${this.name}.insert(...)`,
|
|
132
|
+
args: {
|
|
133
|
+
1: rowOrRows,
|
|
134
|
+
2: param2
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AnyObject, InsertParams } from "prostgles-types";
|
|
2
|
+
import { LocalParams, TableHandler } from "../DboBuilder";
|
|
3
|
+
import { TableRule } from "../PublishParser";
|
|
4
|
+
/**
|
|
5
|
+
* Used for doing referenced inserts within a single transaction
|
|
6
|
+
*/
|
|
7
|
+
export declare function insertDataParse(this: TableHandler, data: (AnyObject | AnyObject[]), param2?: InsertParams, param3_unused?: undefined, tableRules?: TableRule, _localParams?: LocalParams): Promise<{
|
|
8
|
+
data?: AnyObject | AnyObject[];
|
|
9
|
+
insertResult?: AnyObject | AnyObject[];
|
|
10
|
+
}>;
|
|
11
|
+
//# sourceMappingURL=insertDataParse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insertDataParse.d.ts","sourceRoot":"","sources":["insertDataParse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqC,YAAY,EAAgD,MAAM,iBAAiB,CAAC;AAC3I,OAAO,EAA4B,WAAW,EAAmC,YAAY,EAAiB,MAAM,eAAe,CAAC;AACpI,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG7C;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC,EAC/B,MAAM,CAAC,EAAE,YAAY,EACrB,aAAa,CAAC,EAAE,SAAS,EACzB,UAAU,CAAC,EAAE,SAAS,EACtB,YAAY,CAAC,EAAE,WAAW,GACzB,OAAO,CAAC;IACT,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC/B,YAAY,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACxC,CAAC,CAgSD"}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.insertDataParse = void 0;
|
|
4
|
+
const prostgles_types_1 = require("prostgles-types");
|
|
5
|
+
const DboBuilder_1 = require("../DboBuilder");
|
|
6
|
+
const PubSubManager_1 = require("../PubSubManager");
|
|
7
|
+
/**
|
|
8
|
+
* Used for doing referenced inserts within a single transaction
|
|
9
|
+
*/
|
|
10
|
+
async function insertDataParse(data, param2, param3_unused, tableRules, _localParams) {
|
|
11
|
+
const localParams = _localParams || {};
|
|
12
|
+
let dbTX = localParams?.dbTX || this.dbTX;
|
|
13
|
+
const MEDIA_COL_NAMES = ["data", "name"];
|
|
14
|
+
const isMultiInsert = Array.isArray(data);
|
|
15
|
+
const getExtraKeys = (d) => (0, prostgles_types_1.getKeys)(d).filter(k => {
|
|
16
|
+
/* If media then use file insert columns */
|
|
17
|
+
if (this.is_media) {
|
|
18
|
+
return !this.column_names.concat(MEDIA_COL_NAMES).includes(k);
|
|
19
|
+
}
|
|
20
|
+
else if (!this.columns.find(c => c.name === k)) {
|
|
21
|
+
if (!(0, DboBuilder_1.isPojoObject)(d[k]) && !Array.isArray(d[k])) {
|
|
22
|
+
throw new Error("Invalid/Dissalowed field in data: " + k);
|
|
23
|
+
}
|
|
24
|
+
else if (!this.dboBuilder.dbo[k]) {
|
|
25
|
+
throw new Error("Invalid/Dissalowed nested insert table name in data: " + k);
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
});
|
|
31
|
+
const ALLOWED_COL_TYPES = ["int2", "int4", "int8", "numeric", "uuid", "text", "varchar", "char"];
|
|
32
|
+
const getColumnInserts = (d) => (0, prostgles_types_1.getKeys)(d)
|
|
33
|
+
.filter(k => d[k] && (0, DboBuilder_1.isPojoObject)(d[k]))
|
|
34
|
+
.map(k => {
|
|
35
|
+
const insertedCol = this.columns.find(c => c.name === k &&
|
|
36
|
+
ALLOWED_COL_TYPES.includes(c.udt_name));
|
|
37
|
+
if (insertedCol && (insertedCol.references?.fcols.length !== 1 ||
|
|
38
|
+
this.columns.some(c =>
|
|
39
|
+
// c.name !== insertedCol.name && // a bit reduntant: Cannot have one col reference two columns
|
|
40
|
+
c.references &&
|
|
41
|
+
c.references?.ftable === insertedCol.references?.ftable && // same ftable
|
|
42
|
+
c.references?.fcols[0] !== insertedCol.references?.fcols[0] // different fcols
|
|
43
|
+
))) {
|
|
44
|
+
throw "A reference column insert is not possible for multiple column relationships";
|
|
45
|
+
}
|
|
46
|
+
if (insertedCol) {
|
|
47
|
+
return {
|
|
48
|
+
tableName: insertedCol.references.ftable,
|
|
49
|
+
col: insertedCol.name,
|
|
50
|
+
fcol: insertedCol.references.fcols[0]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}).filter(prostgles_types_1.isDefined);
|
|
55
|
+
/**
|
|
56
|
+
* True when: nested table data is provided within
|
|
57
|
+
* [nestedTable] property key
|
|
58
|
+
* OR
|
|
59
|
+
* [referencing_column] property key
|
|
60
|
+
* If true then will do the full insert within this function
|
|
61
|
+
* Nested insert is not allowed for the file table
|
|
62
|
+
* */
|
|
63
|
+
const isNestedInsert = this.is_media ? false : (isMultiInsert ? data : [data]).some(d => getExtraKeys(d).length || getColumnInserts(d).length);
|
|
64
|
+
/**
|
|
65
|
+
* Make sure nested insert uses a transaction
|
|
66
|
+
*/
|
|
67
|
+
if (isNestedInsert && !dbTX) {
|
|
68
|
+
return {
|
|
69
|
+
insertResult: await this.dboBuilder.getTX((dbTX) => dbTX[this.name].insert(data, param2, param3_unused, tableRules, { dbTX, ...localParams }))
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// if(!dbTX && this.t) dbTX = this.d;
|
|
73
|
+
const preValidate = tableRules?.insert?.preValidate, validate = tableRules?.insert?.validate;
|
|
74
|
+
let _data = await Promise.all((isMultiInsert ? data : [data]).map(async (row) => {
|
|
75
|
+
if (preValidate) {
|
|
76
|
+
row = await preValidate(row);
|
|
77
|
+
}
|
|
78
|
+
const dataKeys = Object.keys(row);
|
|
79
|
+
const extraKeys = getExtraKeys(row);
|
|
80
|
+
const colInserts = getColumnInserts(row);
|
|
81
|
+
/* Upload file then continue insert */
|
|
82
|
+
if (this.is_media) {
|
|
83
|
+
if (!this.dboBuilder.prostgles?.fileManager)
|
|
84
|
+
throw "fileManager not set up";
|
|
85
|
+
const { data, name } = row;
|
|
86
|
+
if (dataKeys.length !== 2)
|
|
87
|
+
throw "Expecting only two properties: { name: string; data: File }";
|
|
88
|
+
// if(!Buffer.isBuffer(data)) throw "data is not of type Buffer"
|
|
89
|
+
if (!data)
|
|
90
|
+
throw "data not provided";
|
|
91
|
+
if (typeof name !== "string") {
|
|
92
|
+
throw "name is not of type string";
|
|
93
|
+
}
|
|
94
|
+
const media_id = (await this.db.oneOrNone("SELECT gen_random_uuid() as name")).name;
|
|
95
|
+
const type = await this.dboBuilder.prostgles.fileManager.getMIME(data, name);
|
|
96
|
+
const media_name = `${media_id}.${type.ext}`;
|
|
97
|
+
let media = {
|
|
98
|
+
id: media_id,
|
|
99
|
+
name: media_name,
|
|
100
|
+
original_name: name,
|
|
101
|
+
extension: type.ext,
|
|
102
|
+
content_type: type.mime
|
|
103
|
+
};
|
|
104
|
+
if (validate) {
|
|
105
|
+
media = await validate(media);
|
|
106
|
+
}
|
|
107
|
+
const _media = await this.dboBuilder.prostgles.fileManager.uploadAsMedia({
|
|
108
|
+
item: {
|
|
109
|
+
data,
|
|
110
|
+
name: media.name ?? "????",
|
|
111
|
+
content_type: media.content_type
|
|
112
|
+
},
|
|
113
|
+
// imageCompression: {
|
|
114
|
+
// inside: {
|
|
115
|
+
// width: 1100,
|
|
116
|
+
// height: 630
|
|
117
|
+
// }
|
|
118
|
+
// }
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
...media,
|
|
122
|
+
..._media,
|
|
123
|
+
};
|
|
124
|
+
/* Potentially a nested join */
|
|
125
|
+
}
|
|
126
|
+
else if (extraKeys.length || colInserts.length) {
|
|
127
|
+
/* Ensure we're using the same transaction */
|
|
128
|
+
const _this = this.t ? this : dbTX[this.name];
|
|
129
|
+
const omitedKeys = extraKeys.concat(colInserts.map(c => c.col));
|
|
130
|
+
// let rootData = isMultiInsert? data.map(d => omitKeys(d, omitedKeys)) : omitKeys(data, omitedKeys);
|
|
131
|
+
let rootData = (0, PubSubManager_1.omitKeys)(row, omitedKeys);
|
|
132
|
+
let insertedChildren;
|
|
133
|
+
let targetTableRules;
|
|
134
|
+
/** Insert referenced first and then populate root data with referenced keys */
|
|
135
|
+
if (colInserts.length) {
|
|
136
|
+
for await (const colInsert of colInserts) {
|
|
137
|
+
const colRows = await referencedInsert(_this, dbTX, localParams, colInsert.tableName, row[colInsert.col]);
|
|
138
|
+
if (!Array.isArray(colRows) || colRows.length !== 1 || [null, undefined].includes(colRows[0][colInsert.fcol])) {
|
|
139
|
+
throw new Error("Could not do nested column insert: Unexpected return " + JSON.stringify(colRows));
|
|
140
|
+
}
|
|
141
|
+
const foreignKey = colRows[0][colInsert.fcol];
|
|
142
|
+
rootData[colInsert.col] = foreignKey;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const fullRootResult = await _this.insert(rootData, { returning: "*" }, undefined, tableRules, localParams);
|
|
146
|
+
let returnData;
|
|
147
|
+
const returning = param2?.returning;
|
|
148
|
+
if (returning) {
|
|
149
|
+
returnData = {};
|
|
150
|
+
const returningItems = await this.prepareReturning(returning, this.parseFieldFilter(tableRules?.insert?.returningFields));
|
|
151
|
+
returningItems.filter(s => s.selected).map(rs => {
|
|
152
|
+
returnData[rs.alias] = fullRootResult[rs.alias];
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
await Promise.all(extraKeys.map(async (targetTable) => {
|
|
156
|
+
const childDataItems = Array.isArray(row[targetTable]) ? row[targetTable] : [row[targetTable]];
|
|
157
|
+
const thisInfo = await this.getInfo();
|
|
158
|
+
const childInsert = async (cdata, tableName) => {
|
|
159
|
+
return referencedInsert(this, dbTX, localParams, tableName, cdata);
|
|
160
|
+
};
|
|
161
|
+
const jp = await getJoinPath(this, targetTable);
|
|
162
|
+
const { path } = jp;
|
|
163
|
+
const [tbl1, tbl2, tbl3] = path;
|
|
164
|
+
targetTableRules = await canInsert(this, targetTable, localParams); // tbl3
|
|
165
|
+
const cols2 = this.dboBuilder.dbo[tbl2].columns || [];
|
|
166
|
+
if (!this.dboBuilder.dbo[tbl2])
|
|
167
|
+
throw "Invalid/disallowed table: " + tbl2;
|
|
168
|
+
const colsRefT1 = cols2?.filter(c => c.references?.cols.length === 1 && c.references?.ftable === tbl1);
|
|
169
|
+
if (!path.length) {
|
|
170
|
+
throw "Nested inserts join path not found for " + [this.name, targetTable];
|
|
171
|
+
}
|
|
172
|
+
else if (path.length === 2) {
|
|
173
|
+
if (targetTable !== tbl2)
|
|
174
|
+
throw "Did not expect this";
|
|
175
|
+
if (!colsRefT1.length)
|
|
176
|
+
throw `Target table ${tbl2} does not reference any columns from the root table ${this.name}. Cannot do nested insert`;
|
|
177
|
+
// console.log(childDataItems, JSON.stringify(colsRefT1, null, 2))
|
|
178
|
+
insertedChildren = await childInsert(childDataItems.map((d) => {
|
|
179
|
+
let result = { ...d };
|
|
180
|
+
colsRefT1.map(col => {
|
|
181
|
+
result[col.references.cols[0]] = fullRootResult[col.references.fcols[0]];
|
|
182
|
+
});
|
|
183
|
+
return result;
|
|
184
|
+
}), targetTable);
|
|
185
|
+
// console.log({ insertedChildren })
|
|
186
|
+
}
|
|
187
|
+
else if (path.length === 3) {
|
|
188
|
+
if (targetTable !== tbl3)
|
|
189
|
+
throw "Did not expect this";
|
|
190
|
+
const colsRefT3 = cols2?.filter(c => c.references?.cols.length === 1 && c.references?.ftable === tbl3);
|
|
191
|
+
if (!colsRefT1.length || !colsRefT3.length)
|
|
192
|
+
throw "Incorrectly referenced or missing columns for nested insert";
|
|
193
|
+
const fileTable = this.dboBuilder.prostgles.fileManager?.tableName;
|
|
194
|
+
if (targetTable !== fileTable) {
|
|
195
|
+
throw "Only media allowed to have nested inserts more than 2 tables apart";
|
|
196
|
+
}
|
|
197
|
+
/* We expect tbl2 to have only 2 columns (media_id and foreign_id) */
|
|
198
|
+
if (!cols2 || !(cols2.filter(c => c.references?.ftable === fileTable).length === 1 &&
|
|
199
|
+
cols2.filter(c => c.references?.ftable === _this.name).length === 1)) {
|
|
200
|
+
console.log({ tbl1, tbl2, tbl3, name: _this.name, tthisName: this.name });
|
|
201
|
+
throw "Second joining table not of expected format. Must contain exactly one reference column for each table (file table and target table) ";
|
|
202
|
+
}
|
|
203
|
+
insertedChildren = await childInsert(childDataItems, targetTable);
|
|
204
|
+
/* Insert in key_lookup table */
|
|
205
|
+
await Promise.all(insertedChildren.map(async (t3Child) => {
|
|
206
|
+
let tbl2Row = {};
|
|
207
|
+
colsRefT3.map(col => {
|
|
208
|
+
tbl2Row[col.name] = t3Child[col.references.fcols[0]];
|
|
209
|
+
});
|
|
210
|
+
colsRefT1.map(col => {
|
|
211
|
+
tbl2Row[col.name] = fullRootResult[col.references.fcols[0]];
|
|
212
|
+
});
|
|
213
|
+
// console.log({ rootResult, tbl2Row, t3Child, colsRefT3, colsRefT1, t: this.t?.ctx?.start });
|
|
214
|
+
await childInsert(tbl2Row, tbl2); //.then(() => {});
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
console.error(JSON.stringify({ path, thisTable: this.name, targetTable }, null, 2));
|
|
219
|
+
throw "Unexpected path for Nested inserts";
|
|
220
|
+
}
|
|
221
|
+
/* Return also the nested inserted data */
|
|
222
|
+
if (targetTableRules && insertedChildren?.length && returning) {
|
|
223
|
+
const targetTableHandler = dbTX[targetTable];
|
|
224
|
+
const targetReturning = await targetTableHandler.prepareReturning("*", targetTableHandler.parseFieldFilter(targetTableRules?.insert?.returningFields));
|
|
225
|
+
let clientTargetInserts = insertedChildren.map(d => {
|
|
226
|
+
let _d = { ...d };
|
|
227
|
+
let res = {};
|
|
228
|
+
targetReturning.map(r => {
|
|
229
|
+
res[r.alias] = _d[r.alias];
|
|
230
|
+
});
|
|
231
|
+
return res;
|
|
232
|
+
});
|
|
233
|
+
returnData[targetTable] = clientTargetInserts.length === 1 ? clientTargetInserts[0] : clientTargetInserts;
|
|
234
|
+
}
|
|
235
|
+
}));
|
|
236
|
+
return returnData;
|
|
237
|
+
}
|
|
238
|
+
return row;
|
|
239
|
+
}));
|
|
240
|
+
let result = isMultiInsert ? _data : _data[0];
|
|
241
|
+
// if(validate && !isNestedInsert){
|
|
242
|
+
// result = isMultiInsert? await Promise.all(_data.map(async d => await validate({ ...d }))) : await validate({ ..._data[0] });
|
|
243
|
+
// }
|
|
244
|
+
let res = isNestedInsert ?
|
|
245
|
+
{ insertResult: result } :
|
|
246
|
+
{ data: result };
|
|
247
|
+
return res;
|
|
248
|
+
}
|
|
249
|
+
exports.insertDataParse = insertDataParse;
|
|
250
|
+
/* Must be allowed to insert into referenced table */
|
|
251
|
+
const canInsert = async (tableHandler, targetTable, localParams) => {
|
|
252
|
+
const childRules = await tableHandler.dboBuilder.publishParser?.getValidatedRequestRuleWusr({ tableName: targetTable, command: "insert", localParams });
|
|
253
|
+
if (!childRules || !childRules.insert)
|
|
254
|
+
throw "Dissallowed nested insert into table " + childRules;
|
|
255
|
+
return childRules;
|
|
256
|
+
};
|
|
257
|
+
const getJoinPath = async (tableHandler, targetTable) => {
|
|
258
|
+
const jp = tableHandler.dboBuilder.joinPaths.find(jp => jp.t1 === tableHandler.name && jp.t2 === targetTable);
|
|
259
|
+
if (!jp) {
|
|
260
|
+
console.trace(tableHandler.dboBuilder.joinPaths);
|
|
261
|
+
const pref = tableHandler.dboBuilder.prostgles.opts.joins !== "inferred" ? "Joins are not inferred! " : "";
|
|
262
|
+
throw new Error(`${pref}Could not find a single join path for the nested data ( sourceTable: ${tableHandler.name} targetTable: ${targetTable} ) `);
|
|
263
|
+
}
|
|
264
|
+
return jp;
|
|
265
|
+
};
|
|
266
|
+
const referencedInsert = async (tableHandler, dbTX, localParams, targetTable, targetData) => {
|
|
267
|
+
const thisInfo = await tableHandler.getInfo();
|
|
268
|
+
await getJoinPath(tableHandler, targetTable);
|
|
269
|
+
if (!targetData || !dbTX?.[targetTable] || !("insert" in dbTX[targetTable]))
|
|
270
|
+
throw new Error("childInsertErr: Table handler missing for referenced table: " + targetTable);
|
|
271
|
+
const childRules = await canInsert(tableHandler, targetTable, localParams);
|
|
272
|
+
if (thisInfo.has_media === "one" && thisInfo.media_table_name === targetTable && Array.isArray(targetData) && targetData.length > 1) {
|
|
273
|
+
throw "Constraint check fail: Cannot insert more than one record into " + JSON.stringify(targetTable);
|
|
274
|
+
}
|
|
275
|
+
return Promise.all((Array.isArray(targetData) ? targetData : [targetData])
|
|
276
|
+
.map(m => dbTX[targetTable]
|
|
277
|
+
.insert(m, { returning: "*" }, undefined, childRules, localParams)
|
|
278
|
+
.catch(e => {
|
|
279
|
+
console.trace({ childInsertErr: e });
|
|
280
|
+
return Promise.reject({ childInsertErr: e });
|
|
281
|
+
})));
|
|
282
|
+
};
|