prostgles-server 3.0.70 → 3.0.72

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.
@@ -32,162 +32,16 @@ const parseFunctionObject = (funcData) => {
32
32
  };
33
33
  exports.parseFunctionObject = parseFunctionObject;
34
34
  class SelectItemBuilder {
35
+ select = [];
36
+ allFields;
37
+ allowedFields;
38
+ allowedOrderByFields;
39
+ computedFields;
40
+ functions;
41
+ allowedFieldsIncludingComputed;
42
+ isView;
43
+ columns;
35
44
  constructor(params) {
36
- this.select = [];
37
- this.checkField = (f, isSelected) => {
38
- const allowedSelectedFields = this.allowedFieldsIncludingComputed;
39
- const allowedNonSelectedFields = [...this.allowedFieldsIncludingComputed, ...this.allowedOrderByFields];
40
- /** Not selected items can be part of the orderBy fields */
41
- if (!(isSelected ? allowedSelectedFields : allowedNonSelectedFields).includes(f)) {
42
- throw "Field " + f + " is invalid or dissallowed";
43
- }
44
- return f;
45
- };
46
- this.addItem = (item) => {
47
- let fields = item.getFields();
48
- // console.trace(fields)
49
- if (fields === "*")
50
- fields = this.allowedFields.slice(0);
51
- fields.map(f => this.checkField(f, item.selected));
52
- if (this.select.find(s => s.alias === item.alias)) {
53
- throw `Cannot specify duplicate columns ( ${item.alias} ). Perhaps you're using "*" with column names?`;
54
- }
55
- this.select.push({ ...item, fields });
56
- };
57
- this.addFunction = (func, args, alias) => {
58
- const funcDef = (0, Functions_1.parseFunction)({
59
- func, args, functions: this.functions,
60
- allowedFields: this.allowedFieldsIncludingComputed,
61
- });
62
- this.addItem({
63
- type: funcDef.type,
64
- alias,
65
- getFields: () => funcDef.getFields(args),
66
- getQuery: (tableAlias) => funcDef.getQuery({ allColumns: this.columns, allowedFields: this.allowedFields, args, tableAlias,
67
- ctidField: undefined,
68
- /* CTID not available in AFTER trigger */
69
- // ctidField: this.isView? undefined : "ctid"
70
- }),
71
- selected: true
72
- });
73
- };
74
- this.addColumn = (fieldName, selected) => {
75
- /* Check if computed col */
76
- if (selected) {
77
- const compCol = Functions_1.COMPUTED_FIELDS.find(cf => cf.name === fieldName);
78
- if (compCol && !this.select.find(s => s.alias === fieldName)) {
79
- const cf = {
80
- ...compCol,
81
- type: "computed",
82
- numArgs: 0,
83
- singleColArg: false,
84
- getFields: (args) => []
85
- };
86
- this.addFunction(cf, [], compCol.name);
87
- return;
88
- }
89
- }
90
- const colDef = this.columns.find(c => c.name === fieldName);
91
- let alias = selected ? fieldName : ("not_selected_" + fieldName);
92
- this.addItem({
93
- type: "column",
94
- columnPGDataType: colDef?.data_type,
95
- column_udt_type: colDef?.udt_name,
96
- alias,
97
- getQuery: () => (0, prostgles_types_1.asName)(fieldName),
98
- getFields: () => [fieldName],
99
- selected
100
- });
101
- };
102
- this.parseUserSelect = async (userSelect, joinParse) => {
103
- /* Array select */
104
- if (Array.isArray(userSelect)) {
105
- if (userSelect.find(key => typeof key !== "string"))
106
- throw "Invalid array select. Expecting an array of strings";
107
- userSelect.map(key => this.addColumn(key, true));
108
- /* Empty select */
109
- }
110
- else if (userSelect === "") {
111
- // select.push({
112
- // type: "function",
113
- // alias: "",
114
- // getFields: () => [],
115
- // getQuery: () => ""
116
- // })
117
- return [];
118
- }
119
- else if (userSelect === "*") {
120
- this.allowedFields.map(key => this.addColumn(key, true));
121
- }
122
- else if ((0, DboBuilder_1.isPlainObject)(userSelect) && !(0, prostgles_types_1.isEmpty)(userSelect)) {
123
- const selectKeys = Object.keys(userSelect), selectValues = Object.values(userSelect);
124
- /* Cannot include and exclude at the same time */
125
- if (selectValues.filter(v => [0, false].includes(v)).length) {
126
- if (selectValues.filter(v => ![0, false].includes(v)).length) {
127
- throw "\nCannot include and exclude fields at the same time";
128
- }
129
- /* Exclude only */
130
- this.allowedFields.filter(f => !selectKeys.includes(f)).map(key => this.addColumn(key, true));
131
- }
132
- else {
133
- await Promise.all(selectKeys.map(async (key) => {
134
- const val = userSelect[key], throwErr = (extraErr = "") => {
135
- console.trace(extraErr);
136
- throw "Unexpected select -> " + JSON.stringify({ [key]: val }) + "\n" + extraErr;
137
- };
138
- /* Included fields */
139
- if ([1, true].includes(val)) {
140
- if (key === "*") {
141
- this.allowedFields.map(key => this.addColumn(key, true));
142
- }
143
- else {
144
- this.addColumn(key, true);
145
- }
146
- /* Aggs and functions */
147
- }
148
- else if (typeof val === "string" || (0, DboBuilder_1.isPlainObject)(val)) {
149
- /* Function shorthand notation
150
- { id: "$max" } === { id: { $max: ["id"] } } === SELECT MAX(id) AS id
151
- */
152
- if ((typeof val === "string" && val !== "*") ||
153
- (0, DboBuilder_1.isPlainObject)(val) && Object.keys(val).length === 1 && Array.isArray(Object.values(val)[0]) // !isPlainObject(Object.values(val)[0])
154
- ) {
155
- // if(!Array.isArray(Object.values(val)[0])){
156
- // throw `Could not parse selected item: ${JSON.stringify(val)}\nFunction arguments must be in an array`;
157
- // }
158
- let funcName, args;
159
- if (typeof val === "string") {
160
- /* Shorthand notation -> it is expected that the key is the column name used as the only argument */
161
- try {
162
- this.checkField(key, true);
163
- }
164
- catch (err) {
165
- throwErr(` Shorthand function notation error: the specifield column ( ${key} ) is invalid or dissallowed. \n Use correct column name or full aliased function notation, e.g.: -> { alias: { $func_name: ["column_name"] } } `);
166
- }
167
- funcName = val;
168
- args = [key];
169
- /** Function full notation { $funcName: ["colName", ...args] } */
170
- }
171
- else {
172
- ({ funcName, args } = (0, exports.parseFunctionObject)(val));
173
- }
174
- this.addFunction(funcName, args, key);
175
- /* Join */
176
- }
177
- else {
178
- if (!joinParse)
179
- throw "Joins dissalowed";
180
- await joinParse(key, val, throwErr);
181
- }
182
- }
183
- else
184
- throwErr();
185
- }));
186
- }
187
- }
188
- else
189
- throw "Unexpected select -> " + JSON.stringify(userSelect);
190
- };
191
45
  this.allFields = params.allFields;
192
46
  this.allowedFields = params.allowedFields;
193
47
  this.allowedOrderByFields = params.allowedOrderByFields;
@@ -205,6 +59,160 @@ class SelectItemBuilder {
205
59
  throw "INTERNAL ERROR: Cannot have duplicate column names ( " + conflictingCol + " ). One or more computed column names are colliding with table columns ones";
206
60
  }
207
61
  }
62
+ checkField = (f, isSelected) => {
63
+ const allowedSelectedFields = this.allowedFieldsIncludingComputed;
64
+ const allowedNonSelectedFields = [...this.allowedFieldsIncludingComputed, ...this.allowedOrderByFields];
65
+ /** Not selected items can be part of the orderBy fields */
66
+ if (!(isSelected ? allowedSelectedFields : allowedNonSelectedFields).includes(f)) {
67
+ throw "Field " + f + " is invalid or dissallowed";
68
+ }
69
+ return f;
70
+ };
71
+ addItem = (item) => {
72
+ let fields = item.getFields();
73
+ // console.trace(fields)
74
+ if (fields === "*")
75
+ fields = this.allowedFields.slice(0);
76
+ fields.map(f => this.checkField(f, item.selected));
77
+ if (this.select.find(s => s.alias === item.alias)) {
78
+ throw `Cannot specify duplicate columns ( ${item.alias} ). Perhaps you're using "*" with column names?`;
79
+ }
80
+ this.select.push({ ...item, fields });
81
+ };
82
+ addFunction = (func, args, alias) => {
83
+ const funcDef = (0, Functions_1.parseFunction)({
84
+ func, args, functions: this.functions,
85
+ allowedFields: this.allowedFieldsIncludingComputed,
86
+ });
87
+ this.addItem({
88
+ type: funcDef.type,
89
+ alias,
90
+ getFields: () => funcDef.getFields(args),
91
+ getQuery: (tableAlias) => funcDef.getQuery({ allColumns: this.columns, allowedFields: this.allowedFields, args, tableAlias,
92
+ ctidField: undefined,
93
+ /* CTID not available in AFTER trigger */
94
+ // ctidField: this.isView? undefined : "ctid"
95
+ }),
96
+ selected: true
97
+ });
98
+ };
99
+ addColumn = (fieldName, selected) => {
100
+ /* Check if computed col */
101
+ if (selected) {
102
+ const compCol = Functions_1.COMPUTED_FIELDS.find(cf => cf.name === fieldName);
103
+ if (compCol && !this.select.find(s => s.alias === fieldName)) {
104
+ const cf = {
105
+ ...compCol,
106
+ type: "computed",
107
+ numArgs: 0,
108
+ singleColArg: false,
109
+ getFields: (args) => []
110
+ };
111
+ this.addFunction(cf, [], compCol.name);
112
+ return;
113
+ }
114
+ }
115
+ const colDef = this.columns.find(c => c.name === fieldName);
116
+ let alias = selected ? fieldName : ("not_selected_" + fieldName);
117
+ this.addItem({
118
+ type: "column",
119
+ columnPGDataType: colDef?.data_type,
120
+ column_udt_type: colDef?.udt_name,
121
+ alias,
122
+ getQuery: () => (0, prostgles_types_1.asName)(fieldName),
123
+ getFields: () => [fieldName],
124
+ selected
125
+ });
126
+ };
127
+ parseUserSelect = async (userSelect, joinParse) => {
128
+ /* Array select */
129
+ if (Array.isArray(userSelect)) {
130
+ if (userSelect.find(key => typeof key !== "string"))
131
+ throw "Invalid array select. Expecting an array of strings";
132
+ userSelect.map(key => this.addColumn(key, true));
133
+ /* Empty select */
134
+ }
135
+ else if (userSelect === "") {
136
+ // select.push({
137
+ // type: "function",
138
+ // alias: "",
139
+ // getFields: () => [],
140
+ // getQuery: () => ""
141
+ // })
142
+ return [];
143
+ }
144
+ else if (userSelect === "*") {
145
+ this.allowedFields.map(key => this.addColumn(key, true));
146
+ }
147
+ else if ((0, DboBuilder_1.isPlainObject)(userSelect) && !(0, prostgles_types_1.isEmpty)(userSelect)) {
148
+ const selectKeys = Object.keys(userSelect), selectValues = Object.values(userSelect);
149
+ /* Cannot include and exclude at the same time */
150
+ if (selectValues.filter(v => [0, false].includes(v)).length) {
151
+ if (selectValues.filter(v => ![0, false].includes(v)).length) {
152
+ throw "\nCannot include and exclude fields at the same time";
153
+ }
154
+ /* Exclude only */
155
+ this.allowedFields.filter(f => !selectKeys.includes(f)).map(key => this.addColumn(key, true));
156
+ }
157
+ else {
158
+ await Promise.all(selectKeys.map(async (key) => {
159
+ const val = userSelect[key], throwErr = (extraErr = "") => {
160
+ console.trace(extraErr);
161
+ throw "Unexpected select -> " + JSON.stringify({ [key]: val }) + "\n" + extraErr;
162
+ };
163
+ /* Included fields */
164
+ if ([1, true].includes(val)) {
165
+ if (key === "*") {
166
+ this.allowedFields.map(key => this.addColumn(key, true));
167
+ }
168
+ else {
169
+ this.addColumn(key, true);
170
+ }
171
+ /* Aggs and functions */
172
+ }
173
+ else if (typeof val === "string" || (0, DboBuilder_1.isPlainObject)(val)) {
174
+ /* Function shorthand notation
175
+ { id: "$max" } === { id: { $max: ["id"] } } === SELECT MAX(id) AS id
176
+ */
177
+ if ((typeof val === "string" && val !== "*") ||
178
+ (0, DboBuilder_1.isPlainObject)(val) && Object.keys(val).length === 1 && Array.isArray(Object.values(val)[0]) // !isPlainObject(Object.values(val)[0])
179
+ ) {
180
+ // if(!Array.isArray(Object.values(val)[0])){
181
+ // throw `Could not parse selected item: ${JSON.stringify(val)}\nFunction arguments must be in an array`;
182
+ // }
183
+ let funcName, args;
184
+ if (typeof val === "string") {
185
+ /* Shorthand notation -> it is expected that the key is the column name used as the only argument */
186
+ try {
187
+ this.checkField(key, true);
188
+ }
189
+ catch (err) {
190
+ throwErr(` Shorthand function notation error: the specifield column ( ${key} ) is invalid or dissallowed. \n Use correct column name or full aliased function notation, e.g.: -> { alias: { $func_name: ["column_name"] } } `);
191
+ }
192
+ funcName = val;
193
+ args = [key];
194
+ /** Function full notation { $funcName: ["colName", ...args] } */
195
+ }
196
+ else {
197
+ ({ funcName, args } = (0, exports.parseFunctionObject)(val));
198
+ }
199
+ this.addFunction(funcName, args, key);
200
+ /* Join */
201
+ }
202
+ else {
203
+ if (!joinParse)
204
+ throw "Joins dissalowed";
205
+ await joinParse(key, val, throwErr);
206
+ }
207
+ }
208
+ else
209
+ throwErr();
210
+ }));
211
+ }
212
+ }
213
+ else
214
+ throw "Unexpected select -> " + JSON.stringify(userSelect);
215
+ };
208
216
  }
209
217
  exports.SelectItemBuilder = SelectItemBuilder;
210
218
  async function getNewQuery(_this, filter, selectParams = {}, param3_unused = null, tableRules, localParams, columns) {
@@ -12,28 +12,9 @@ const ViewHandler_1 = require("./ViewHandler");
12
12
  const parseUpdateRules_1 = require("./parseUpdateRules");
13
13
  const Functions_1 = require("./QueryBuilder/Functions");
14
14
  class TableHandler extends ViewHandler_1.ViewHandler {
15
+ io_stats;
15
16
  constructor(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths) {
16
17
  super(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths);
17
- this.parseUpdateRules = parseUpdateRules_1.parseUpdateRules.bind(this);
18
- this.update = update_1.update.bind(this);
19
- this.insertDataParse = insertDataParse_1.insertDataParse;
20
- this.prepareReturning = async (returning, allowedFields) => {
21
- let result = [];
22
- if (returning) {
23
- let sBuilder = new QueryBuilder_1.SelectItemBuilder({
24
- allFields: this.column_names.slice(0),
25
- allowedFields,
26
- allowedOrderByFields: allowedFields,
27
- computedFields: Functions_1.COMPUTED_FIELDS,
28
- functions: Functions_1.FUNCTIONS.filter(f => f.type === "function" && f.singleColArg),
29
- isView: this.is_view,
30
- columns: this.columns,
31
- });
32
- await sBuilder.parseUserSelect(returning);
33
- return sBuilder.select;
34
- }
35
- return result;
36
- };
37
18
  this.remove = this.delete;
38
19
  this.io_stats = {
39
20
  since: Date.now(),
@@ -73,6 +54,8 @@ class TableHandler extends ViewHandler_1.ViewHandler {
73
54
  throw (0, DboBuilder_1.parseError)(e, `dbo.${this.name}.update()`);
74
55
  }
75
56
  }
57
+ parseUpdateRules = parseUpdateRules_1.parseUpdateRules.bind(this);
58
+ update = update_1.update.bind(this);
76
59
  validateNewData({ row, forcedData, allowedFields, tableRules, fixIssues = false }) {
77
60
  const synced_field = (tableRules ?? {})?.sync?.synced_field;
78
61
  /* Update synced_field if sync is on and missing */
@@ -95,9 +78,27 @@ class TableHandler extends ViewHandler_1.ViewHandler {
95
78
  });
96
79
  return { data, allowedCols: this.columns.filter(c => dataKeys.includes(c.name)).map(c => c.name) };
97
80
  }
81
+ insertDataParse = insertDataParse_1.insertDataParse;
98
82
  async insert(rowOrRows, param2, param3_unused, tableRules, _localParams) {
99
83
  return insert_1.insert.bind(this)(rowOrRows, param2, param3_unused, tableRules, _localParams);
100
84
  }
85
+ prepareReturning = async (returning, allowedFields) => {
86
+ let result = [];
87
+ if (returning) {
88
+ let sBuilder = new QueryBuilder_1.SelectItemBuilder({
89
+ allFields: this.column_names.slice(0),
90
+ allowedFields,
91
+ allowedOrderByFields: allowedFields,
92
+ computedFields: Functions_1.COMPUTED_FIELDS,
93
+ functions: Functions_1.FUNCTIONS.filter(f => f.type === "function" && f.singleColArg),
94
+ isView: this.is_view,
95
+ columns: this.columns,
96
+ });
97
+ await sBuilder.parseUserSelect(returning);
98
+ return sBuilder.select;
99
+ }
100
+ return result;
101
+ };
101
102
  makeReturnQuery(items) {
102
103
  if (items?.length)
103
104
  return " RETURNING " + items.map(s => s.getQuery() + " AS " + (0, prostgles_types_1.asName)(s.alias)).join(", ");
@@ -13,6 +13,7 @@ const getColumns_1 = require("./getColumns");
13
13
  const subscribe_1 = require("./subscribe");
14
14
  const FILTER_FUNCS = Functions_1.FUNCTIONS.filter(f => f.canBeUsedForFilter);
15
15
  class ColSet {
16
+ opts;
16
17
  constructor(columns, tableName) {
17
18
  this.opts = { columns, tableName, colNames: columns.map(c => c.name) };
18
19
  }
@@ -99,14 +100,26 @@ class ColSet {
99
100
  }
100
101
  }
101
102
  class ViewHandler {
103
+ db;
104
+ name;
105
+ escapedName;
106
+ columns;
107
+ columnsForTypes;
108
+ column_names;
109
+ tableOrViewInfo; // TableOrViewInfo;
110
+ colSet;
111
+ tsColumnDefs = [];
112
+ joins;
113
+ joinGraph;
114
+ joinPaths;
115
+ dboBuilder;
116
+ t;
117
+ dbTX;
118
+ is_view = true;
119
+ filterDef = "";
120
+ // pubSubManager: PubSubManager;
121
+ is_media = false;
102
122
  constructor(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths) {
103
- this.tsColumnDefs = [];
104
- this.is_view = true;
105
- this.filterDef = "";
106
- // pubSubManager: PubSubManager;
107
- this.is_media = false;
108
- // TODO: fix renamed table trigger problem
109
- this.getColumns = getColumns_1.getColumns.bind(this);
110
123
  if (!db || !tableOrViewInfo)
111
124
  throw "";
112
125
  this.db = db;
@@ -282,7 +295,7 @@ class ViewHandler {
282
295
  /* Make the join chain info excluding root table */
283
296
  paths = (path || jp.path).slice(1).map((t2, i, arr) => {
284
297
  const t1 = i === 0 ? source : arr[i - 1];
285
- this.joins ?? (this.joins = this.dboBuilder.joins);
298
+ this.joins ??= this.dboBuilder.joins;
286
299
  /* Get join options */
287
300
  const jo = this.joins.find(j => j.tables.includes(t1) && j.tables.includes(t2));
288
301
  if (!jo)
@@ -378,6 +391,8 @@ class ViewHandler {
378
391
  }
379
392
  };
380
393
  }
394
+ // TODO: fix renamed table trigger problem
395
+ getColumns = getColumns_1.getColumns.bind(this);
381
396
  getValidatedRules(tableRules, localParams) {
382
397
  if (localParams?.socket && !tableRules) {
383
398
  throw "INTERNAL ERROR: Unexpected case -> localParams && !tableRules";
@@ -13,8 +13,8 @@ async function runSQL(query, params, options, localParams) {
13
13
  }
14
14
  }
15
15
  /** Cache types */
16
- this.DATA_TYPES ?? (this.DATA_TYPES = await this.db.any("SELECT oid, typname FROM pg_type") ?? []);
17
- this.USER_TABLES ?? (this.USER_TABLES = await this.db.any(`
16
+ this.DATA_TYPES ??= await this.db.any("SELECT oid, typname FROM pg_type") ?? [];
17
+ this.USER_TABLES ??= await this.db.any(`
18
18
  SELECT relid, relname, schemaname, array_to_json(array_agg(c.column_name) FILTER (WHERE c.column_name IS NOT NULL)) as pkey_columns
19
19
  FROM pg_catalog.pg_statio_user_tables t
20
20
  LEFT JOIN (
@@ -26,13 +26,13 @@ async function runSQL(query, params, options, localParams) {
26
26
  ) c
27
27
  ON t.relid = c.table_oid
28
28
  GROUP BY relid, relname, schemaname
29
- `) ?? []);
30
- this.USER_TABLE_COLUMNS ?? (this.USER_TABLE_COLUMNS = await this.db.any(`
29
+ `) ?? [];
30
+ this.USER_TABLE_COLUMNS ??= await this.db.any(`
31
31
  SELECT t.relid, t.schemaname,t.relname, c.column_name, c.udt_name, c.ordinal_position
32
32
  FROM information_schema.columns c
33
33
  INNER JOIN pg_catalog.pg_statio_user_tables t
34
34
  ON c.table_schema = t.schemaname AND c.table_name = t.relname
35
- `));
35
+ `);
36
36
  if (!(await (0, exports.canRunSQL)(this.prostgles, localParams)))
37
37
  throw "Not allowed to run SQL";
38
38
  const { returnType, allowListen, hasParams = true } = options || {};