prostgles-server 4.2.624 → 4.2.626
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/Audit/AuditTable.d.ts +4 -2
- package/dist/Audit/AuditTable.d.ts.map +1 -1
- package/dist/Audit/AuditTable.js +3 -2
- package/dist/Audit/AuditTable.js.map +1 -1
- package/dist/Audit/getAuditTableRules.d.ts +7 -0
- package/dist/Audit/getAuditTableRules.d.ts.map +1 -0
- package/dist/Audit/getAuditTableRules.js +191 -0
- package/dist/Audit/getAuditTableRules.js.map +1 -0
- package/dist/Audit/getAuditTriggerConfig.d.ts.map +1 -1
- package/dist/Audit/getAuditTriggerConfig.js +14 -1
- package/dist/Audit/getAuditTriggerConfig.js.map +1 -1
- package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.d.ts.map +1 -1
- package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.js +7 -1
- package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.js.map +1 -1
- package/dist/DBSchemaBuilder/getDBGeneratedSchema.d.ts.map +1 -1
- package/dist/DBSchemaBuilder/getDBGeneratedSchema.js +1 -2
- package/dist/DBSchemaBuilder/getDBGeneratedSchema.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts +10 -5
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.js +90 -71
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getJoinQuery.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getJoinQuery.js +3 -2
- package/dist/DboBuilder/QueryBuilder/getJoinQuery.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts +2 -2
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js +53 -116
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getQuerySource.d.ts +18 -0
- package/dist/DboBuilder/QueryBuilder/getQuerySource.d.ts.map +1 -0
- package/dist/DboBuilder/QueryBuilder/getQuerySource.js +29 -0
- package/dist/DboBuilder/QueryBuilder/getQuerySource.js.map +1 -0
- package/dist/DboBuilder/QueryBuilder/getSelectQuery.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getSelectQuery.js +6 -2
- package/dist/DboBuilder/QueryBuilder/getSelectQuery.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/parseJoinSelect.d.ts +13 -0
- package/dist/DboBuilder/QueryBuilder/parseJoinSelect.d.ts.map +1 -0
- package/dist/DboBuilder/QueryBuilder/parseJoinSelect.js +57 -0
- package/dist/DboBuilder/QueryBuilder/parseJoinSelect.js.map +1 -0
- package/dist/DboBuilder/TableHandler/TableHandler.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/TableHandler.js +2 -1
- package/dist/DboBuilder/TableHandler/TableHandler.js.map +1 -1
- package/dist/DboBuilder/TableHandler/delete.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/delete.js +2 -1
- package/dist/DboBuilder/TableHandler/delete.js.map +1 -1
- package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts +3 -1
- package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js +19 -5
- package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js.map +1 -1
- package/dist/DboBuilder/TableHandler/runInsertUpdateQuery.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/runInsertUpdateQuery.js +2 -1
- package/dist/DboBuilder/TableHandler/runInsertUpdateQuery.js.map +1 -1
- package/dist/DboBuilder/TableHandler/update.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/update.js +2 -1
- package/dist/DboBuilder/TableHandler/update.js.map +1 -1
- package/dist/DboBuilder/TableHandler/updateFile.d.ts +2 -39
- package/dist/DboBuilder/TableHandler/updateFile.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/updateFile.js +1 -0
- package/dist/DboBuilder/TableHandler/updateFile.js.map +1 -1
- package/dist/DboBuilder/TableHandler/uploadFile.d.ts +2 -1
- package/dist/DboBuilder/TableHandler/uploadFile.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/uploadFile.js +2 -1
- package/dist/DboBuilder/TableHandler/uploadFile.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +6 -16
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.js +2 -7
- package/dist/DboBuilder/ViewHandler/ViewHandler.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/find.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/find.js +1 -1
- package/dist/DboBuilder/ViewHandler/find.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/getExistsCondition.d.ts +1 -1
- package/dist/DboBuilder/ViewHandler/getExistsCondition.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/getExistsCondition.js +24 -13
- package/dist/DboBuilder/ViewHandler/getExistsCondition.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/getRawInfo.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/getRawInfo.js +6 -2
- package/dist/DboBuilder/ViewHandler/getRawInfo.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/getTableJoinQuery.d.ts +4 -1
- package/dist/DboBuilder/ViewHandler/getTableJoinQuery.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/getTableJoinQuery.js +20 -14
- package/dist/DboBuilder/ViewHandler/getTableJoinQuery.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/prepareWhere.d.ts +7 -3
- package/dist/DboBuilder/ViewHandler/prepareWhere.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/prepareWhere.js +30 -23
- package/dist/DboBuilder/ViewHandler/prepareWhere.js.map +1 -1
- package/dist/DboBuilder/getColumns.d.ts.map +1 -1
- package/dist/DboBuilder/getColumns.js +3 -0
- package/dist/DboBuilder/getColumns.js.map +1 -1
- package/dist/DboBuilder/getCondition.d.ts +2 -1
- package/dist/DboBuilder/getCondition.d.ts.map +1 -1
- package/dist/DboBuilder/getCondition.js +9 -8
- package/dist/DboBuilder/getCondition.js.map +1 -1
- package/dist/DboBuilder/getSubscribeRelatedTables.d.ts.map +1 -1
- package/dist/DboBuilder/getSubscribeRelatedTables.js +3 -2
- package/dist/DboBuilder/getSubscribeRelatedTables.js.map +1 -1
- package/dist/DboBuilder/parseUpdateRules.d.ts.map +1 -1
- package/dist/DboBuilder/parseUpdateRules.js +3 -2
- package/dist/DboBuilder/parseUpdateRules.js.map +1 -1
- package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.d.ts +2 -2
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +2 -0
- package/dist/Prostgles.js.map +1 -1
- package/dist/ProstglesContext.spec.js +25 -0
- package/dist/ProstglesContext.spec.js.map +1 -1
- package/dist/ProstglesTypes.d.ts +10 -0
- package/dist/ProstglesTypes.d.ts.map +1 -1
- package/dist/ProstglesTypes.js.map +1 -1
- package/dist/PubSubManager/PubSubManager.d.ts +1 -1
- package/dist/PublishParser/PublishParser.d.ts.map +1 -1
- package/dist/PublishParser/PublishParser.js +22 -4
- package/dist/PublishParser/PublishParser.js.map +1 -1
- package/dist/PublishParser/applyScopeToTableRules.d.ts +0 -1
- package/dist/PublishParser/applyScopeToTableRules.d.ts.map +1 -1
- package/dist/PublishParser/applyScopeToTableRules.js +3 -7
- package/dist/PublishParser/applyScopeToTableRules.js.map +1 -1
- package/dist/PublishParser/getFileTableRules.d.ts.map +1 -1
- package/dist/PublishParser/getFileTableRules.js +4 -1
- package/dist/PublishParser/getFileTableRules.js.map +1 -1
- package/dist/PublishParser/getFileVersionTableRules.d.ts +7 -0
- package/dist/PublishParser/getFileVersionTableRules.d.ts.map +1 -0
- package/dist/PublishParser/getFileVersionTableRules.js +77 -0
- package/dist/PublishParser/getFileVersionTableRules.js.map +1 -0
- package/dist/PublishParser/getFileVersionTableRules.spec.d.ts +2 -0
- package/dist/PublishParser/getFileVersionTableRules.spec.d.ts.map +1 -0
- package/dist/PublishParser/getFileVersionTableRules.spec.js +50 -0
- package/dist/PublishParser/getFileVersionTableRules.spec.js.map +1 -0
- package/dist/PublishParser/getPublishedTableNames.d.ts.map +1 -1
- package/dist/PublishParser/getPublishedTableNames.js +24 -1
- package/dist/PublishParser/getPublishedTableNames.js.map +1 -1
- package/dist/PublishParser/publishTypesAndUtils.d.ts +0 -4
- package/dist/PublishParser/publishTypesAndUtils.d.ts.map +1 -1
- package/dist/PublishParser/publishTypesAndUtils.js.map +1 -1
- package/dist/StorageClient/deleteUnreferencedFile.d.ts.map +1 -1
- package/dist/StorageClient/deleteUnreferencedFile.js +17 -1
- package/dist/StorageClient/deleteUnreferencedFile.js.map +1 -1
- package/dist/StorageClient/fileTableDefinitions.d.ts +54 -0
- package/dist/StorageClient/fileTableDefinitions.d.ts.map +1 -0
- package/dist/StorageClient/fileTableDefinitions.js +38 -0
- package/dist/StorageClient/fileTableDefinitions.js.map +1 -0
- package/dist/StorageClient/fileVersionUtils.d.ts +18 -0
- package/dist/StorageClient/fileVersionUtils.d.ts.map +1 -0
- package/dist/StorageClient/fileVersionUtils.js +22 -0
- package/dist/StorageClient/fileVersionUtils.js.map +1 -0
- package/dist/StorageClient/getFileStorageKey.d.ts +3 -1
- package/dist/StorageClient/getFileStorageKey.d.ts.map +1 -1
- package/dist/StorageClient/getFileStorageKey.js +9 -1
- package/dist/StorageClient/getFileStorageKey.js.map +1 -1
- package/dist/StorageClient/getFileTableConfig.d.ts +1 -23
- package/dist/StorageClient/getFileTableConfig.d.ts.map +1 -1
- package/dist/StorageClient/getFileTableConfig.js +63 -25
- package/dist/StorageClient/getFileTableConfig.js.map +1 -1
- package/dist/StorageClient/getFileTableConfig.spec.js +6 -1
- package/dist/StorageClient/getFileTableConfig.spec.js.map +1 -1
- package/dist/StorageClient/saveFileVersion.d.ts +7 -0
- package/dist/StorageClient/saveFileVersion.d.ts.map +1 -0
- package/dist/StorageClient/saveFileVersion.js +71 -0
- package/dist/StorageClient/saveFileVersion.js.map +1 -0
- package/dist/StorageClient/setupFileServeHandler.d.ts.map +1 -1
- package/dist/StorageClient/setupFileServeHandler.js +38 -21
- package/dist/StorageClient/setupFileServeHandler.js.map +1 -1
- package/dist/TableConfig/TableRowFromColumnDefinitions.d.ts +11 -4
- package/dist/TableConfig/TableRowFromColumnDefinitions.d.ts.map +1 -1
- package/dist/TableConfig/getColumnSQLDefinitionQuery.d.ts.map +1 -1
- package/dist/TableConfig/getColumnSQLDefinitionQuery.js +1 -2
- package/dist/TableConfig/getColumnSQLDefinitionQuery.js.map +1 -1
- package/dist/TableConfig/initTableConfig.d.ts.map +1 -1
- package/dist/TableConfig/initTableConfig.js +9 -6
- package/dist/TableConfig/initTableConfig.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/lib/Audit/AuditTable.ts +8 -4
- package/lib/Audit/getAuditTableRules.ts +292 -0
- package/lib/Audit/getAuditTriggerConfig.ts +14 -1
- package/lib/DBSchemaBuilder/getClientDBGeneratedSchemas.ts +9 -1
- package/lib/DBSchemaBuilder/getDBGeneratedSchema.ts +1 -2
- package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +118 -90
- package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +3 -2
- package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +111 -194
- package/lib/DboBuilder/QueryBuilder/getQuerySource.ts +55 -0
- package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +8 -2
- package/lib/DboBuilder/QueryBuilder/parseJoinSelect.ts +73 -0
- package/lib/DboBuilder/TableHandler/TableHandler.ts +3 -6
- package/lib/DboBuilder/TableHandler/delete.ts +2 -1
- package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +31 -5
- package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +2 -1
- package/lib/DboBuilder/TableHandler/update.ts +2 -1
- package/lib/DboBuilder/TableHandler/updateFile.ts +1 -0
- package/lib/DboBuilder/TableHandler/uploadFile.ts +3 -0
- package/lib/DboBuilder/ViewHandler/ViewHandler.ts +6 -12
- package/lib/DboBuilder/ViewHandler/find.ts +0 -1
- package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +35 -28
- package/lib/DboBuilder/ViewHandler/getRawInfo.ts +6 -2
- package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +22 -14
- package/lib/DboBuilder/ViewHandler/prepareWhere.ts +48 -27
- package/lib/DboBuilder/getColumns.ts +2 -0
- package/lib/DboBuilder/getCondition.ts +11 -10
- package/lib/DboBuilder/getSubscribeRelatedTables.ts +3 -2
- package/lib/DboBuilder/parseUpdateRules.ts +3 -2
- package/lib/Prostgles.ts +1 -0
- package/lib/ProstglesContext.spec.ts +30 -0
- package/lib/ProstglesTypes.ts +11 -0
- package/lib/PublishParser/PublishParser.ts +47 -4
- package/lib/PublishParser/applyScopeToTableRules.ts +1 -7
- package/lib/PublishParser/getFileTableRules.ts +7 -1
- package/lib/PublishParser/getFileVersionTableRules.spec.ts +57 -0
- package/lib/PublishParser/getFileVersionTableRules.ts +106 -0
- package/lib/PublishParser/getPublishedTableNames.ts +33 -2
- package/lib/PublishParser/publishTypesAndUtils.ts +0 -5
- package/lib/StorageClient/deleteUnreferencedFile.ts +20 -2
- package/lib/StorageClient/fileTableDefinitions.ts +52 -0
- package/lib/StorageClient/fileVersionUtils.ts +37 -0
- package/lib/StorageClient/getFileStorageKey.ts +11 -2
- package/lib/StorageClient/getFileTableConfig.spec.ts +6 -1
- package/lib/StorageClient/getFileTableConfig.ts +85 -30
- package/lib/StorageClient/saveFileVersion.ts +82 -0
- package/lib/StorageClient/setupFileServeHandler.ts +51 -28
- package/lib/TableConfig/TableRowFromColumnDefinitions.ts +37 -13
- package/lib/TableConfig/getColumnSQLDefinitionQuery.ts +8 -2
- package/lib/TableConfig/initTableConfig.ts +10 -6
- package/lib/index.ts +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { as } from "pg-promise";
|
|
2
|
+
import { asName, fromEntries, isDefined, isEmpty, isObject } from "prostgles-types";
|
|
3
|
+
import type { AuthClientRequest, AuthResultWithSID } from "../Auth/AuthTypes";
|
|
4
|
+
import type { Filter, LocalParams } from "../DboBuilder/DboBuilder";
|
|
5
|
+
import type { ViewHandler } from "../DboBuilder/ViewHandler/ViewHandler";
|
|
6
|
+
import { withTableExpression } from "../DboBuilder/QueryBuilder/getQuerySource";
|
|
7
|
+
import type { PublishParser } from "../PublishParser/PublishParser";
|
|
8
|
+
import type {
|
|
9
|
+
ParsedPublishTable,
|
|
10
|
+
PublishObject,
|
|
11
|
+
SelectRule,
|
|
12
|
+
} from "../PublishParser/publishTypesAndUtils";
|
|
13
|
+
import { AUDIT_TABLE_COLUMN_NAMES, type AuditTableRow } from "./AuditTable";
|
|
14
|
+
import type { ResolvedAuditConfig } from "./AuditTypes";
|
|
15
|
+
import { prepareWhere } from "../DboBuilder/ViewHandler/prepareWhere";
|
|
16
|
+
|
|
17
|
+
export const getAuditTableRules = async function (
|
|
18
|
+
this: PublishParser,
|
|
19
|
+
audit: ResolvedAuditConfig,
|
|
20
|
+
auditTablePublishRules: ParsedPublishTable | undefined,
|
|
21
|
+
clientReq: AuthClientRequest | undefined,
|
|
22
|
+
clientInfo: AuthResultWithSID | undefined,
|
|
23
|
+
scope: LocalParams["scope"] | undefined,
|
|
24
|
+
resolvedPublishObject: PublishObject | undefined,
|
|
25
|
+
): Promise<ParsedPublishTable | undefined> {
|
|
26
|
+
if (auditTablePublishRules?.select) {
|
|
27
|
+
return auditTablePublishRules;
|
|
28
|
+
}
|
|
29
|
+
if (resolvedPublishObject && Object.hasOwn(resolvedPublishObject, audit.tableName)) {
|
|
30
|
+
return auditTablePublishRules;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const auditedTableNames = Object.keys(audit.tables).filter(
|
|
34
|
+
(tableName) => canSelect(resolvedPublishObject?.[tableName]) || this.dbo[tableName]?.is_media,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const auditTableHandler = this.dbo[audit.tableName]!;
|
|
38
|
+
|
|
39
|
+
const auditedTableRules = (
|
|
40
|
+
await Promise.all(
|
|
41
|
+
auditedTableNames.map(async (tableName) => {
|
|
42
|
+
const tableHandler = this.dbo[tableName];
|
|
43
|
+
if (!tableHandler) return;
|
|
44
|
+
|
|
45
|
+
const rules = await this.getTableRules({
|
|
46
|
+
tableName,
|
|
47
|
+
clientReq,
|
|
48
|
+
clientInfo,
|
|
49
|
+
scope,
|
|
50
|
+
resolvedPublishObject,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!rules?.select) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
handler: tableHandler,
|
|
59
|
+
additionalForcedCondition: await getSourceAuditCondition(
|
|
60
|
+
tableHandler,
|
|
61
|
+
rules.select.forcedFilter,
|
|
62
|
+
audit.tables[tableName]!.excludeColumns,
|
|
63
|
+
),
|
|
64
|
+
tableColumnOverrides: getAuditRowColumnOverrides(
|
|
65
|
+
tableHandler.parseFieldFilter(rules.select.fields),
|
|
66
|
+
audit.tables[tableName]!.idColumns,
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
}),
|
|
70
|
+
)
|
|
71
|
+
).filter(isDefined);
|
|
72
|
+
|
|
73
|
+
if (!auditedTableRules.length) {
|
|
74
|
+
return auditTablePublishRules;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const validateRequest = (filter: Filter) => {
|
|
78
|
+
const requiredTableNameFilterValue = getRequiredStringFilter(
|
|
79
|
+
filter,
|
|
80
|
+
[AUDIT_TABLE_COLUMN_NAMES.schema_name, AUDIT_TABLE_COLUMN_NAMES.table_name],
|
|
81
|
+
this.prostgles.keywords.$and,
|
|
82
|
+
);
|
|
83
|
+
if (!requiredTableNameFilterValue) {
|
|
84
|
+
throw new Error("Audit queries require an exact schema_name and table_name filter");
|
|
85
|
+
}
|
|
86
|
+
const { schema_name, table_name } = requiredTableNameFilterValue;
|
|
87
|
+
const [matchingAuditedTableRules, ...otherMatches] = auditedTableRules.filter(
|
|
88
|
+
({ handler: { tableOrViewInfo } }) => {
|
|
89
|
+
const sourceName = tableOrViewInfo.qualifiedNameParts;
|
|
90
|
+
return sourceName.name === table_name && sourceName.schema === schema_name;
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
if (otherMatches.length) {
|
|
94
|
+
throw new Error("Invalid audit table filter: multiple matching rules found");
|
|
95
|
+
}
|
|
96
|
+
if (!matchingAuditedTableRules) {
|
|
97
|
+
throw new Error("Invalid audit table filter: no matching rules found");
|
|
98
|
+
}
|
|
99
|
+
const { additionalForcedCondition, tableColumnOverrides } = matchingAuditedTableRules;
|
|
100
|
+
const tableExpression = getTableExpression(
|
|
101
|
+
auditTableHandler,
|
|
102
|
+
tableColumnOverrides,
|
|
103
|
+
additionalForcedCondition,
|
|
104
|
+
);
|
|
105
|
+
return { tableExpression };
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const select: SelectRule = withTableExpression(
|
|
109
|
+
{
|
|
110
|
+
fields: "*",
|
|
111
|
+
filterFields: "*",
|
|
112
|
+
orderByFields: "*",
|
|
113
|
+
disableMethods: { subscribe: 1, sync: 1 },
|
|
114
|
+
},
|
|
115
|
+
(request) => validateRequest(request.filter).tableExpression,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return { ...auditTablePublishRules, select };
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const getAuditRowColumnOverrides = (allowedColumns: string[], idColumns: readonly string[]) => {
|
|
122
|
+
type SnapshotColumn =
|
|
123
|
+
| typeof AUDIT_TABLE_COLUMN_NAMES.old_id
|
|
124
|
+
| typeof AUDIT_TABLE_COLUMN_NAMES.new_id
|
|
125
|
+
| typeof AUDIT_TABLE_COLUMN_NAMES.old_row
|
|
126
|
+
| typeof AUDIT_TABLE_COLUMN_NAMES.new_row;
|
|
127
|
+
const getSnapshotExpression = (snapshotColumn: SnapshotColumn, columns: string[]) => {
|
|
128
|
+
const snapshot = asName(snapshotColumn);
|
|
129
|
+
const args = columns.flatMap((columnName) => [
|
|
130
|
+
as.text(columnName),
|
|
131
|
+
`${snapshot} -> ${as.text(columnName)}`,
|
|
132
|
+
]);
|
|
133
|
+
return `CASE WHEN ${snapshot} IS NULL THEN NULL ELSE jsonb_build_object(${args.join(", ")}) END`;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
[AUDIT_TABLE_COLUMN_NAMES.old_id]: getSnapshotExpression(
|
|
138
|
+
AUDIT_TABLE_COLUMN_NAMES.old_id,
|
|
139
|
+
idColumns.filter((columnName) => allowedColumns.includes(columnName)),
|
|
140
|
+
),
|
|
141
|
+
[AUDIT_TABLE_COLUMN_NAMES.new_id]: getSnapshotExpression(
|
|
142
|
+
AUDIT_TABLE_COLUMN_NAMES.new_id,
|
|
143
|
+
idColumns.filter((columnName) => allowedColumns.includes(columnName)),
|
|
144
|
+
),
|
|
145
|
+
[AUDIT_TABLE_COLUMN_NAMES.old_row]: getSnapshotExpression(
|
|
146
|
+
AUDIT_TABLE_COLUMN_NAMES.old_row,
|
|
147
|
+
allowedColumns,
|
|
148
|
+
),
|
|
149
|
+
[AUDIT_TABLE_COLUMN_NAMES.new_row]: getSnapshotExpression(
|
|
150
|
+
AUDIT_TABLE_COLUMN_NAMES.new_row,
|
|
151
|
+
allowedColumns,
|
|
152
|
+
),
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const getSourceAuditCondition = async (
|
|
157
|
+
sourceHandler: ViewHandler,
|
|
158
|
+
forcedFilter: Filter | undefined,
|
|
159
|
+
excludedColumns: readonly string[],
|
|
160
|
+
) => {
|
|
161
|
+
const { schema, name } = sourceHandler.tableOrViewInfo.qualifiedNameParts;
|
|
162
|
+
const auditColumn = (columnName: keyof AuditTableRow) => asName(columnName);
|
|
163
|
+
const sourceCondition = [
|
|
164
|
+
`${auditColumn(AUDIT_TABLE_COLUMN_NAMES.schema_name)} = ${as.text(schema)}`,
|
|
165
|
+
`${auditColumn(AUDIT_TABLE_COLUMN_NAMES.table_name)} = ${as.text(name)}`,
|
|
166
|
+
].join(" AND ");
|
|
167
|
+
if (!forcedFilter || !Object.keys(forcedFilter).length) {
|
|
168
|
+
return sourceCondition;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const getSnapshotCondition = async (
|
|
172
|
+
snapshotColumn:
|
|
173
|
+
typeof AUDIT_TABLE_COLUMN_NAMES.old_row | typeof AUDIT_TABLE_COLUMN_NAMES.new_row,
|
|
174
|
+
) => {
|
|
175
|
+
const alias = `prostgles_audit_${snapshotColumn}`;
|
|
176
|
+
const snapshot = auditColumn(snapshotColumn);
|
|
177
|
+
const { condition } = await prepareWhere(sourceHandler, {
|
|
178
|
+
filter: forcedFilter,
|
|
179
|
+
select: undefined,
|
|
180
|
+
filterFields: sourceHandler.column_names.filter(
|
|
181
|
+
(columnName) => !excludedColumns.includes(columnName),
|
|
182
|
+
),
|
|
183
|
+
addWhere: false,
|
|
184
|
+
tableAlias: { raw: alias, escaped: asName(alias) },
|
|
185
|
+
localParams: undefined,
|
|
186
|
+
tableRule: undefined,
|
|
187
|
+
});
|
|
188
|
+
if (!condition) return "TRUE";
|
|
189
|
+
|
|
190
|
+
const record = `NULL::${sourceHandler.escapedName}`;
|
|
191
|
+
return `(
|
|
192
|
+
${snapshot} IS NULL OR EXISTS (
|
|
193
|
+
SELECT 1
|
|
194
|
+
FROM jsonb_populate_record(${record}, ${snapshot}) AS ${asName(alias)}
|
|
195
|
+
WHERE ${condition}
|
|
196
|
+
)
|
|
197
|
+
)`;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const [oldRowCondition, newRowCondition] = await Promise.all([
|
|
201
|
+
getSnapshotCondition(AUDIT_TABLE_COLUMN_NAMES.old_row),
|
|
202
|
+
getSnapshotCondition(AUDIT_TABLE_COLUMN_NAMES.new_row),
|
|
203
|
+
]);
|
|
204
|
+
/** Prevent updates that cross a forcedFilter boundary from exposing either snapshot. */
|
|
205
|
+
return `(${sourceCondition}) AND ${oldRowCondition} AND ${newRowCondition}`;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const canSelect = (rule: PublishObject[string] | undefined) => {
|
|
209
|
+
return rule === "*" || rule === true || (isObject(rule) && Boolean(rule.select));
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const getRequiredStringFilter = <Key extends keyof AuditTableRow>(
|
|
213
|
+
filter: Filter,
|
|
214
|
+
fieldNames: Key[],
|
|
215
|
+
andKey: string,
|
|
216
|
+
): Record<Key, string> | undefined => {
|
|
217
|
+
const values = getExactStringFilters(filter, fieldNames, andKey);
|
|
218
|
+
if (fieldNames.some((fieldName) => !values[fieldName])) return;
|
|
219
|
+
return values as Record<Key, string>;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const getExactStringFilters = <Key extends keyof AuditTableRow>(
|
|
223
|
+
filter: Filter,
|
|
224
|
+
fieldNames: Key[],
|
|
225
|
+
andKey: string,
|
|
226
|
+
): Partial<Record<Key, string>> => {
|
|
227
|
+
if (!isObject(filter)) return {};
|
|
228
|
+
const filterRecord = filter as Record<string, unknown>;
|
|
229
|
+
const result = fromEntries(
|
|
230
|
+
fieldNames
|
|
231
|
+
.map((fieldName) => {
|
|
232
|
+
const value = filterRecord[fieldName];
|
|
233
|
+
if (typeof value === "string") return [fieldName, value] as const;
|
|
234
|
+
if (!isObject(value)) return;
|
|
235
|
+
|
|
236
|
+
const entries = Object.entries(value);
|
|
237
|
+
const [operator, exactValue] = entries[0] ?? [];
|
|
238
|
+
if (
|
|
239
|
+
entries.length === 1 &&
|
|
240
|
+
operator &&
|
|
241
|
+
["=", "$eq"].includes(operator) &&
|
|
242
|
+
typeof exactValue === "string"
|
|
243
|
+
) {
|
|
244
|
+
return [fieldName, exactValue] as const;
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
.filter(isDefined),
|
|
248
|
+
) as Partial<Record<Key, string>>;
|
|
249
|
+
|
|
250
|
+
const andFilters = filterRecord[andKey];
|
|
251
|
+
if (!Array.isArray(andFilters)) return result;
|
|
252
|
+
|
|
253
|
+
for (const andFilter of andFilters) {
|
|
254
|
+
const nestedValues = getExactStringFilters(andFilter as Filter, fieldNames, andKey);
|
|
255
|
+
for (const fieldName of fieldNames) {
|
|
256
|
+
const nestedValue = nestedValues[fieldName];
|
|
257
|
+
if (!nestedValue) continue;
|
|
258
|
+
const existingValue = result[fieldName];
|
|
259
|
+
if (existingValue && existingValue !== nestedValue) {
|
|
260
|
+
throw new Error(`Conflicting exact ${fieldName} filters`);
|
|
261
|
+
}
|
|
262
|
+
result[fieldName] = nestedValue;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const getTableExpression = (
|
|
269
|
+
auditTableHandler: ViewHandler,
|
|
270
|
+
columnOverrides: Record<string, string> | undefined,
|
|
271
|
+
additionalCondition: string | undefined,
|
|
272
|
+
) => {
|
|
273
|
+
const overrides = columnOverrides && !isEmpty(columnOverrides) ? columnOverrides : undefined;
|
|
274
|
+
if (!overrides && !additionalCondition) return;
|
|
275
|
+
|
|
276
|
+
const invalidColumns = Object.keys(overrides ?? {}).filter(
|
|
277
|
+
(columnName) => !auditTableHandler.column_names.includes(columnName),
|
|
278
|
+
);
|
|
279
|
+
if (invalidColumns.length) {
|
|
280
|
+
throw new Error(`Invalid table column overrides: ${invalidColumns.join(", ")}`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const columns =
|
|
284
|
+
overrides ?
|
|
285
|
+
auditTableHandler.column_names.map((columnName) => {
|
|
286
|
+
const override = overrides[columnName];
|
|
287
|
+
return override ? `${override} AS ${asName(columnName)}` : asName(columnName);
|
|
288
|
+
})
|
|
289
|
+
: ["*"];
|
|
290
|
+
const where = additionalCondition ? ` WHERE ${additionalCondition}` : "";
|
|
291
|
+
return `(SELECT ${columns.join(", ")} FROM ${auditTableHandler.escapedName}${where})`;
|
|
292
|
+
};
|
|
@@ -3,9 +3,22 @@ import { asName } from "prostgles-types";
|
|
|
3
3
|
import type { TableConfig } from "../TableConfig/TableConfigTypes";
|
|
4
4
|
import type { ResolvedAuditConfig } from "./AuditTypes";
|
|
5
5
|
import { getAuditProtection, getAuditWriterName } from "./getAuditTableConfig";
|
|
6
|
+
import { AUDIT_TABLE_COLUMN_NAMES } from "./AuditTable";
|
|
6
7
|
|
|
7
8
|
export const getAuditTriggerConfig = (audit: ResolvedAuditConfig): TableConfig => {
|
|
8
9
|
const result: TableConfig = {};
|
|
10
|
+
const columns = AUDIT_TABLE_COLUMN_NAMES;
|
|
11
|
+
const insertColumns = [
|
|
12
|
+
columns.schema_name,
|
|
13
|
+
columns.table_name,
|
|
14
|
+
columns.operation,
|
|
15
|
+
columns.old_id,
|
|
16
|
+
columns.new_id,
|
|
17
|
+
columns.old_row,
|
|
18
|
+
columns.new_row,
|
|
19
|
+
columns.actor,
|
|
20
|
+
columns.db_context,
|
|
21
|
+
].map((columnName) => asName(columnName));
|
|
9
22
|
for (const [name, options] of Object.entries(audit.tables)) {
|
|
10
23
|
const { idColumns: ids, excludeColumns: excluded } = options;
|
|
11
24
|
const functionName = getAuditWriterName(audit.tableName, name);
|
|
@@ -32,7 +45,7 @@ export const getAuditTriggerConfig = (audit: ResolvedAuditConfig): TableConfig =
|
|
|
32
45
|
after_row := after_row - excluded;
|
|
33
46
|
END IF;
|
|
34
47
|
IF TG_OP = 'UPDATE' AND before_row IS NOT DISTINCT FROM after_row THEN RETURN NULL; END IF;
|
|
35
|
-
INSERT INTO ${asName(audit.tableName)} (
|
|
48
|
+
INSERT INTO ${asName(audit.tableName)} (${insertColumns.join(", ")})
|
|
36
49
|
VALUES (TG_TABLE_SCHEMA, TG_TABLE_NAME, TG_OP, before_id, after_id, before_row, after_row,
|
|
37
50
|
nullif(current_setting('prostgles.user', true), '')::jsonb,
|
|
38
51
|
jsonb_build_object(
|
|
@@ -86,6 +86,9 @@ export const getClientDBGeneratedSchemas = (
|
|
|
86
86
|
` ${JSON.stringify(table.name)}: ${tableType} & { insertColumns: ${input}; updateColumns: ${updateInput};${optionalTable} };`,
|
|
87
87
|
];
|
|
88
88
|
});
|
|
89
|
+
if (!definitions.length) {
|
|
90
|
+
return `export type ${name} = Record<string, never>;`;
|
|
91
|
+
}
|
|
89
92
|
return `export type ${name} = {\n${definitions.join("\n")}\n};`;
|
|
90
93
|
};
|
|
91
94
|
|
|
@@ -105,9 +108,14 @@ const reservedNames = new Set(Object.values(DB_GENERATED_NAMES) as string[]);
|
|
|
105
108
|
|
|
106
109
|
export const validateClientSchemaName = (name: string) => {
|
|
107
110
|
// Requiring a Schema suffix also avoids TypeScript keywords and primitive type names.
|
|
108
|
-
if (!/^[A-Za-z_$][\w$]*Schema$/.test(name)
|
|
111
|
+
if (!/^[A-Za-z_$][\w$]*Schema$/.test(name)) {
|
|
109
112
|
throw new Error(
|
|
110
113
|
`Invalid client schema name: ${JSON.stringify(name)}. Use a unique name ending in Schema.`,
|
|
111
114
|
);
|
|
112
115
|
}
|
|
116
|
+
if (reservedNames.has(name)) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Invalid client schema name: ${JSON.stringify(name)}. Use a unique name ending in Schema that does not conflict with reserved names (${Array.from(reservedNames).join(", ")}).`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
113
121
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { isDefined, isEmpty, type TableSchema } from "prostgles-types";
|
|
2
|
-
import { fromEntries } from "../PublishParser/applyScopeToTableRules";
|
|
1
|
+
import { fromEntries, isDefined, isEmpty, type TableSchema } from "prostgles-types";
|
|
3
2
|
import type { TableConfig } from "../TableConfig/TableConfigTypes";
|
|
4
3
|
import { escapeTSNames } from "../utils/utils";
|
|
5
4
|
import { DB_GENERATED_NAMES } from "./constants";
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
7
|
ColumnInfo,
|
|
8
|
-
JoinSelect,
|
|
9
8
|
PG_COLUMN_UDT_DATA_TYPE,
|
|
10
9
|
Select,
|
|
11
10
|
ValidatedColumnInfo,
|
|
@@ -13,12 +12,15 @@ import type {
|
|
|
13
12
|
import { getKeys, isEmpty, isObject, postgresToTsType } from "prostgles-types";
|
|
14
13
|
import type { PGIdentifier, SortItem } from "../DboBuilder";
|
|
15
14
|
|
|
15
|
+
import type { Awaitable } from "../../PublishParser/publishTypesAndUtils";
|
|
16
|
+
import { asNameAlias } from "../../utils/asNameAlias";
|
|
16
17
|
import type { ParsedJoinPath } from "../ViewHandler/parseJoinPath";
|
|
17
|
-
import type {
|
|
18
|
-
import type { FieldSpec, FunctionSpec } from "./Functions/Functions";
|
|
18
|
+
import type { WhereOptions } from "../ViewHandler/prepareWhere";
|
|
19
19
|
import { COMPUTED_FIELDS } from "./Functions/COMPUTED_FIELDS";
|
|
20
|
+
import type { FieldSpec, FunctionSpec } from "./Functions/Functions";
|
|
20
21
|
import { parseFunction } from "./Functions/parseFunction";
|
|
21
|
-
import {
|
|
22
|
+
import { parseJoinSelect, type ParsedJoin } from "./parseJoinSelect";
|
|
23
|
+
import type { QuerySource } from "./getQuerySource";
|
|
22
24
|
|
|
23
25
|
export type SelectItem = {
|
|
24
26
|
getFields: (args: any[]) => string[] | "*";
|
|
@@ -36,13 +38,9 @@ export type SelectItem = {
|
|
|
36
38
|
| {
|
|
37
39
|
type: "function" | "aggregation" | "joinedColumn" | "computed";
|
|
38
40
|
columnName?: undefined;
|
|
39
|
-
// args: any[];
|
|
40
|
-
// getFields: (args: any[]) => string[] | "*";
|
|
41
|
-
// columnNames: string[];
|
|
42
41
|
}
|
|
43
42
|
);
|
|
44
43
|
export type SelectItemValidated = Omit<SelectItem, "getFields"> & { fields: string[] };
|
|
45
|
-
export type WhereOptions = Awaited<ReturnType<ViewHandler["prepareWhere"]>>;
|
|
46
44
|
export type NewQueryRoot = {
|
|
47
45
|
/**
|
|
48
46
|
* All fields from the table will be in nested SELECT and GROUP BY to allow order/filter by fields not in select
|
|
@@ -55,6 +53,7 @@ export type NewQueryRoot = {
|
|
|
55
53
|
select: SelectItemValidated[];
|
|
56
54
|
|
|
57
55
|
table: PGIdentifier;
|
|
56
|
+
source: QuerySource;
|
|
58
57
|
where: string;
|
|
59
58
|
whereOpts: WhereOptions;
|
|
60
59
|
orderByItems: SortItem[];
|
|
@@ -148,7 +147,7 @@ export class SelectItemBuilder {
|
|
|
148
147
|
const allowedFields = isSelected ? allowedSelectedFields : allowedNonSelectedFields;
|
|
149
148
|
if (!allowedFields.includes(f)) {
|
|
150
149
|
throw (
|
|
151
|
-
"Field " + f + " is invalid or
|
|
150
|
+
"Field " + f + " is invalid or disallowed. \nAllowed fields: " + allowedFields.join(", ")
|
|
152
151
|
);
|
|
153
152
|
}
|
|
154
153
|
return f;
|
|
@@ -193,7 +192,7 @@ export class SelectItemBuilder {
|
|
|
193
192
|
});
|
|
194
193
|
};
|
|
195
194
|
|
|
196
|
-
addColumn = (fieldName: string, selected: boolean) => {
|
|
195
|
+
private addColumn = (fieldName: string, selected: boolean) => {
|
|
197
196
|
/* Check if computed col */
|
|
198
197
|
if (selected) {
|
|
199
198
|
const compCol = COMPUTED_FIELDS.find((cf) => cf.name === fieldName);
|
|
@@ -225,10 +224,19 @@ export class SelectItemBuilder {
|
|
|
225
224
|
});
|
|
226
225
|
};
|
|
227
226
|
|
|
228
|
-
|
|
227
|
+
parse = async (
|
|
229
228
|
userSelect: Select,
|
|
230
|
-
joinParse?: (key: string,
|
|
231
|
-
) => {
|
|
229
|
+
joinParse?: (key: string, parsedJoin: ParsedJoin) => Awaitable<void>,
|
|
230
|
+
): Promise<void> => {
|
|
231
|
+
if (userSelect === "") {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (userSelect === "*") {
|
|
236
|
+
this.allowedFields.map((key) => this.addColumn(key, true));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
232
240
|
/* [col1, col2, col3] */
|
|
233
241
|
if (Array.isArray(userSelect)) {
|
|
234
242
|
if (userSelect.find((key) => typeof (key as unknown) !== "string")) {
|
|
@@ -236,90 +244,110 @@ export class SelectItemBuilder {
|
|
|
236
244
|
}
|
|
237
245
|
|
|
238
246
|
userSelect.map((key) => this.addColumn(key, true));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
239
249
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
this.allowedFields
|
|
257
|
-
.filter((f) => !selectKeys.includes(f))
|
|
258
|
-
.map((key) => this.addColumn(key, true));
|
|
259
|
-
} else {
|
|
260
|
-
await Promise.all(
|
|
261
|
-
selectKeys.map(async (key) => {
|
|
262
|
-
const val: unknown = userSelect[key as keyof typeof userSelect];
|
|
263
|
-
const throwErr = (extraErr = "") => {
|
|
264
|
-
console.trace(extraErr);
|
|
265
|
-
throw "Unexpected select -> " + JSON.stringify({ [key]: val }) + "\n" + extraErr;
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
/* Included fields */
|
|
269
|
-
if ([1, true].includes(val as number | boolean)) {
|
|
270
|
-
if (key === "*") {
|
|
271
|
-
this.allowedFields.map((key) => this.addColumn(key, true));
|
|
272
|
-
} else {
|
|
273
|
-
this.addColumn(key, true);
|
|
274
|
-
}
|
|
250
|
+
if (!isObject(userSelect)) {
|
|
251
|
+
throw "Unexpected select -> " + JSON.stringify(userSelect);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (isEmpty(userSelect)) {
|
|
255
|
+
throw "Unexpected empty object select";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const selectKeys = Object.keys(userSelect),
|
|
259
|
+
selectValues = Object.values(userSelect);
|
|
260
|
+
|
|
261
|
+
/* Cannot include and exclude at the same time */
|
|
262
|
+
if (selectValues.filter((v) => [0, false].includes(v as number)).length) {
|
|
263
|
+
if (selectValues.filter((v) => ![0, false].includes(v as number)).length) {
|
|
264
|
+
throw "\nCannot include and exclude fields at the same time";
|
|
265
|
+
}
|
|
275
266
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
267
|
+
/* Exclude only */
|
|
268
|
+
this.allowedFields
|
|
269
|
+
.filter((f) => !selectKeys.includes(f))
|
|
270
|
+
.map((key) => this.addColumn(key, true));
|
|
271
|
+
} else {
|
|
272
|
+
await Promise.all(
|
|
273
|
+
selectKeys.map(async (key) => {
|
|
274
|
+
const val: unknown = userSelect[key as keyof typeof userSelect];
|
|
275
|
+
const throwErr: (message: string) => never = (message) => {
|
|
276
|
+
console.trace(message);
|
|
277
|
+
throw "Unexpected select -> " + JSON.stringify({ [key]: val }) + "\n" + message;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/* Included fields */
|
|
281
|
+
if ([1, true].includes(val as number | boolean)) {
|
|
282
|
+
if (key === "*") {
|
|
283
|
+
this.allowedFields.map((key) => this.addColumn(key, true));
|
|
284
|
+
} else {
|
|
285
|
+
this.addColumn(key, true);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Aggregations and functions */
|
|
289
|
+
} else if (typeof val === "string" || isObject(val)) {
|
|
290
|
+
/* Function shorthand notation
|
|
279
291
|
{ id: "$max" } === { id: { $max: ["id"] } } === SELECT MAX(id) AS id
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
funcName = val;
|
|
298
|
-
args = [key];
|
|
299
|
-
|
|
300
|
-
/** Function full notation { $funcName: ["colName", ...args] } */
|
|
301
|
-
} else {
|
|
302
|
-
({ funcName, args } = parseFunctionObject(val));
|
|
292
|
+
*/
|
|
293
|
+
if (
|
|
294
|
+
(typeof val === "string" && val !== "*") ||
|
|
295
|
+
(isObject(val) &&
|
|
296
|
+
Object.keys(val).length === 1 &&
|
|
297
|
+
Array.isArray(Object.values(val)[0]))
|
|
298
|
+
) {
|
|
299
|
+
let funcName: string | undefined, args: any[] | undefined;
|
|
300
|
+
if (typeof val === "string") {
|
|
301
|
+
/* Shorthand notation -> it is expected that the key is the column name used as the only argument */
|
|
302
|
+
try {
|
|
303
|
+
this.checkField(key, true);
|
|
304
|
+
} catch {
|
|
305
|
+
throwErr(
|
|
306
|
+
` Shorthand function notation error: the specified column ( ${key} ) is invalid or disallowed. \n Use correct column name or full aliased function notation, e.g.: -> { alias: { $func_name: ["column_name"] } } `,
|
|
307
|
+
);
|
|
303
308
|
}
|
|
309
|
+
funcName = val;
|
|
310
|
+
args = [key];
|
|
304
311
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
/* Join */
|
|
312
|
+
/** Function full notation { $funcName: ["colName", ...args] } */
|
|
308
313
|
} else {
|
|
309
|
-
|
|
310
|
-
throw "Joins disallowed";
|
|
311
|
-
}
|
|
312
|
-
await joinParse(key, val as JoinSelect, throwErr);
|
|
314
|
+
({ funcName, args } = parseFunctionObject(val));
|
|
313
315
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
316
|
+
|
|
317
|
+
this.addFunction(funcName, args, key);
|
|
318
|
+
|
|
319
|
+
/* Join */
|
|
320
|
+
} else {
|
|
321
|
+
if (!joinParse) {
|
|
322
|
+
throw "Joins disallowed";
|
|
323
|
+
}
|
|
324
|
+
const joinSelect = val;
|
|
325
|
+
|
|
326
|
+
const parsedJoin = parseJoinSelect(joinSelect);
|
|
327
|
+
|
|
328
|
+
if (typeof parsedJoin === "string") {
|
|
329
|
+
throwErr(parsedJoin);
|
|
330
|
+
}
|
|
331
|
+
await joinParse(key, parsedJoin);
|
|
332
|
+
}
|
|
333
|
+
} else throwErr("Invalid select value");
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
323
336
|
}
|
|
337
|
+
|
|
338
|
+
if (!joinParse) return;
|
|
339
|
+
/**
|
|
340
|
+
* Is this still needed?!!!
|
|
341
|
+
* Add non selected columns
|
|
342
|
+
* This ensures all fields are available for orderBy in case of nested select
|
|
343
|
+
* */
|
|
344
|
+
Array.from(new Set([...this.allowedFields, ...this.allowedOrderByFields])).map((columnName) => {
|
|
345
|
+
if (!this.select.find((s) => s.alias === columnName && s.type === "column")) {
|
|
346
|
+
this.addColumn(columnName, false);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
324
349
|
};
|
|
350
|
+
|
|
351
|
+
parseUserSelect = (userSelect: Select) => this.parse(userSelect);
|
|
352
|
+
parseUserSelectWithJoins = this.parse;
|
|
325
353
|
}
|
|
@@ -255,6 +255,7 @@ const getInnerJoinQuery = ({
|
|
|
255
255
|
|
|
256
256
|
const isFirst = !i;
|
|
257
257
|
if (isFirst) {
|
|
258
|
+
const tableExpression = isLast ? q2.source.expression : table.name;
|
|
258
259
|
return [
|
|
259
260
|
`SELECT `,
|
|
260
261
|
` /* Join fields + select */`,
|
|
@@ -262,13 +263,13 @@ const getInnerJoinQuery = ({
|
|
|
262
263
|
rootSelectItems.map((s) => s.query),
|
|
263
264
|
{ appendCommas: true },
|
|
264
265
|
),
|
|
265
|
-
`FROM ${
|
|
266
|
+
`FROM ${tableExpression} ${table.alias}`,
|
|
266
267
|
...targetQueryExtraQueries,
|
|
267
268
|
];
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
return [
|
|
271
|
-
`INNER JOIN ${table.name} ${table.alias}`,
|
|
272
|
+
`INNER JOIN ${isLast ? q2.source.expression : table.name} ${table.alias}`,
|
|
272
273
|
`ON ${getJoinOnCondition({
|
|
273
274
|
on: path.on,
|
|
274
275
|
leftAlias: prevTable.alias,
|