@warlock.js/cascade 4.0.92 → 4.0.93
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/cjs/contracts/database-driver.contract.d.ts +118 -0
- package/cjs/contracts/database-driver.contract.d.ts.map +1 -1
- package/cjs/contracts/migration-driver.contract.d.ts +14 -0
- package/cjs/contracts/migration-driver.contract.d.ts.map +1 -1
- package/cjs/contracts/query-builder.contract.d.ts +410 -1
- package/cjs/contracts/query-builder.contract.d.ts.map +1 -1
- package/cjs/data-source/data-source-registry.d.ts +4 -0
- package/cjs/data-source/data-source-registry.d.ts.map +1 -1
- package/cjs/data-source/data-source-registry.js +7 -0
- package/cjs/data-source/data-source-registry.js.map +1 -1
- package/cjs/drivers/mongodb/mongodb-blueprint.d.ts.map +1 -0
- package/cjs/drivers/mongodb/mongodb-blueprint.js.map +1 -0
- package/{esm/drivers/mongo → cjs/drivers/mongodb}/mongodb-driver.d.ts +49 -0
- package/cjs/drivers/mongodb/mongodb-driver.d.ts.map +1 -0
- package/cjs/drivers/{mongo → mongodb}/mongodb-driver.js +125 -8
- package/cjs/drivers/mongodb/mongodb-driver.js.map +1 -0
- package/cjs/drivers/{mongo/mongo-id-generator.d.ts → mongodb/mongodb-id-generator.d.ts} +1 -1
- package/cjs/drivers/mongodb/mongodb-id-generator.d.ts.map +1 -0
- package/cjs/drivers/{mongo/mongo-id-generator.js → mongodb/mongodb-id-generator.js} +1 -1
- package/cjs/drivers/mongodb/mongodb-id-generator.js.map +1 -0
- package/cjs/drivers/{mongo/mongo-migration-driver.d.ts → mongodb/mongodb-migration-driver.d.ts} +10 -1
- package/cjs/drivers/mongodb/mongodb-migration-driver.d.ts.map +1 -0
- package/cjs/drivers/{mongo/mongo-migration-driver.js → mongodb/mongodb-migration-driver.js} +18 -1
- package/cjs/drivers/mongodb/mongodb-migration-driver.js.map +1 -0
- package/cjs/drivers/{mongo/mongo-query-builder.d.ts → mongodb/mongodb-query-builder.d.ts} +172 -4
- package/cjs/drivers/mongodb/mongodb-query-builder.d.ts.map +1 -0
- package/cjs/drivers/{mongo/mongo-query-builder.js → mongodb/mongodb-query-builder.js} +220 -14
- package/cjs/drivers/mongodb/mongodb-query-builder.js.map +1 -0
- package/{esm/drivers/mongo/mongo-query-operations.d.ts → cjs/drivers/mongodb/mongodb-query-operations.d.ts} +16 -16
- package/cjs/drivers/mongodb/mongodb-query-operations.d.ts.map +1 -0
- package/cjs/drivers/{mongo/mongo-query-operations.js → mongodb/mongodb-query-operations.js} +22 -22
- package/cjs/drivers/mongodb/mongodb-query-operations.js.map +1 -0
- package/cjs/drivers/{mongo/mongo-query-parser.d.ts → mongodb/mongodb-query-parser.d.ts} +2 -2
- package/cjs/drivers/mongodb/mongodb-query-parser.d.ts.map +1 -0
- package/cjs/drivers/{mongo/mongo-query-parser.js → mongodb/mongodb-query-parser.js} +87 -87
- package/cjs/drivers/mongodb/mongodb-query-parser.js.map +1 -0
- package/cjs/drivers/{mongo/mongo-sync-adapter.d.ts → mongodb/mongodb-sync-adapter.d.ts} +2 -2
- package/cjs/drivers/mongodb/mongodb-sync-adapter.d.ts.map +1 -0
- package/cjs/drivers/{mongo/mongo-sync-adapter.js → mongodb/mongodb-sync-adapter.js} +2 -2
- package/cjs/drivers/mongodb/mongodb-sync-adapter.js.map +1 -0
- package/{esm/drivers/mongo → cjs/drivers/mongodb}/types.d.ts +2 -2
- package/cjs/drivers/mongodb/types.d.ts.map +1 -0
- package/cjs/drivers/postgres/index.d.ts +16 -0
- package/cjs/drivers/postgres/index.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-blueprint.d.ts +64 -0
- package/cjs/drivers/postgres/postgres-blueprint.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-blueprint.js +121 -0
- package/cjs/drivers/postgres/postgres-blueprint.js.map +1 -0
- package/cjs/drivers/postgres/postgres-dialect.d.ts +135 -0
- package/cjs/drivers/postgres/postgres-dialect.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-dialect.js +245 -0
- package/cjs/drivers/postgres/postgres-dialect.js.map +1 -0
- package/cjs/drivers/postgres/postgres-driver.d.ts +360 -0
- package/cjs/drivers/postgres/postgres-driver.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-driver.js +763 -0
- package/cjs/drivers/postgres/postgres-driver.js.map +1 -0
- package/cjs/drivers/postgres/postgres-migration-driver.d.ts +297 -0
- package/cjs/drivers/postgres/postgres-migration-driver.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-migration-driver.js +578 -0
- package/cjs/drivers/postgres/postgres-migration-driver.js.map +1 -0
- package/cjs/drivers/postgres/postgres-query-builder.d.ts +824 -0
- package/cjs/drivers/postgres/postgres-query-builder.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-query-builder.js +1800 -0
- package/cjs/drivers/postgres/postgres-query-builder.js.map +1 -0
- package/cjs/drivers/postgres/postgres-query-parser.d.ts +308 -0
- package/cjs/drivers/postgres/postgres-query-parser.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-query-parser.js +706 -0
- package/cjs/drivers/postgres/postgres-query-parser.js.map +1 -0
- package/cjs/drivers/postgres/postgres-sync-adapter.d.ts +83 -0
- package/cjs/drivers/postgres/postgres-sync-adapter.d.ts.map +1 -0
- package/cjs/drivers/postgres/postgres-sync-adapter.js +197 -0
- package/cjs/drivers/postgres/postgres-sync-adapter.js.map +1 -0
- package/cjs/drivers/postgres/types.d.ts +142 -0
- package/cjs/drivers/postgres/types.d.ts.map +1 -0
- package/cjs/drivers/sql/index.d.ts +10 -0
- package/cjs/drivers/sql/index.d.ts.map +1 -0
- package/cjs/drivers/sql/sql-dialect.contract.d.ts +203 -0
- package/cjs/drivers/sql/sql-dialect.contract.d.ts.map +1 -0
- package/cjs/drivers/sql/sql-types.d.ts +202 -0
- package/cjs/drivers/sql/sql-types.d.ts.map +1 -0
- package/cjs/index.d.ts +9 -6
- package/cjs/index.d.ts.map +1 -1
- package/cjs/index.js +1 -1
- package/cjs/migration/migration-runner.d.ts.map +1 -1
- package/cjs/migration/migration-runner.js +3 -0
- package/cjs/migration/migration-runner.js.map +1 -1
- package/cjs/model/model.d.ts +236 -1
- package/cjs/model/model.d.ts.map +1 -1
- package/cjs/model/model.js +203 -4
- package/cjs/model/model.js.map +1 -1
- package/cjs/relations/helpers.d.ts +156 -0
- package/cjs/relations/helpers.d.ts.map +1 -0
- package/cjs/relations/helpers.js +197 -0
- package/cjs/relations/helpers.js.map +1 -0
- package/cjs/relations/index.d.ts +33 -0
- package/cjs/relations/index.d.ts.map +1 -0
- package/cjs/relations/pivot-operations.d.ts +160 -0
- package/cjs/relations/pivot-operations.d.ts.map +1 -0
- package/cjs/relations/pivot-operations.js +293 -0
- package/cjs/relations/pivot-operations.js.map +1 -0
- package/cjs/relations/relation-loader.d.ts +194 -0
- package/cjs/relations/relation-loader.d.ts.map +1 -0
- package/cjs/relations/relation-loader.js +466 -0
- package/cjs/relations/relation-loader.js.map +1 -0
- package/cjs/relations/types.d.ts +280 -0
- package/cjs/relations/types.d.ts.map +1 -0
- package/cjs/sync/model-sync-operation.js +1 -1
- package/cjs/sync/model-sync-operation.js.map +1 -1
- package/cjs/utils/connect-to-database.d.ts.map +1 -1
- package/cjs/utils/connect-to-database.js +15 -3
- package/cjs/utils/connect-to-database.js.map +1 -1
- package/cjs/utils/define-model.d.ts +51 -29
- package/cjs/utils/define-model.d.ts.map +1 -1
- package/cjs/validation/rules/database-model-rule.js +1 -1
- package/cjs/validation/rules/database-model-rule.js.map +1 -1
- package/esm/contracts/database-driver.contract.d.ts +118 -0
- package/esm/contracts/database-driver.contract.d.ts.map +1 -1
- package/esm/contracts/migration-driver.contract.d.ts +14 -0
- package/esm/contracts/migration-driver.contract.d.ts.map +1 -1
- package/esm/contracts/query-builder.contract.d.ts +410 -1
- package/esm/contracts/query-builder.contract.d.ts.map +1 -1
- package/esm/data-source/data-source-registry.d.ts +4 -0
- package/esm/data-source/data-source-registry.d.ts.map +1 -1
- package/esm/data-source/data-source-registry.js +7 -0
- package/esm/data-source/data-source-registry.js.map +1 -1
- package/esm/drivers/mongodb/mongodb-blueprint.d.ts.map +1 -0
- package/esm/drivers/mongodb/mongodb-blueprint.js.map +1 -0
- package/{cjs/drivers/mongo → esm/drivers/mongodb}/mongodb-driver.d.ts +49 -0
- package/esm/drivers/mongodb/mongodb-driver.d.ts.map +1 -0
- package/esm/drivers/{mongo → mongodb}/mongodb-driver.js +121 -4
- package/esm/drivers/mongodb/mongodb-driver.js.map +1 -0
- package/esm/drivers/{mongo/mongo-id-generator.d.ts → mongodb/mongodb-id-generator.d.ts} +1 -1
- package/esm/drivers/mongodb/mongodb-id-generator.d.ts.map +1 -0
- package/esm/drivers/{mongo/mongo-id-generator.js → mongodb/mongodb-id-generator.js} +1 -1
- package/esm/drivers/mongodb/mongodb-id-generator.js.map +1 -0
- package/esm/drivers/{mongo/mongo-migration-driver.d.ts → mongodb/mongodb-migration-driver.d.ts} +10 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.d.ts.map +1 -0
- package/esm/drivers/{mongo/mongo-migration-driver.js → mongodb/mongodb-migration-driver.js} +18 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.js.map +1 -0
- package/esm/drivers/{mongo/mongo-query-builder.d.ts → mongodb/mongodb-query-builder.d.ts} +172 -4
- package/esm/drivers/mongodb/mongodb-query-builder.d.ts.map +1 -0
- package/esm/drivers/{mongo/mongo-query-builder.js → mongodb/mongodb-query-builder.js} +218 -12
- package/esm/drivers/mongodb/mongodb-query-builder.js.map +1 -0
- package/{cjs/drivers/mongo/mongo-query-operations.d.ts → esm/drivers/mongodb/mongodb-query-operations.d.ts} +16 -16
- package/esm/drivers/mongodb/mongodb-query-operations.d.ts.map +1 -0
- package/esm/drivers/{mongo/mongo-query-operations.js → mongodb/mongodb-query-operations.js} +22 -22
- package/esm/drivers/mongodb/mongodb-query-operations.js.map +1 -0
- package/esm/drivers/{mongo/mongo-query-parser.d.ts → mongodb/mongodb-query-parser.d.ts} +2 -2
- package/esm/drivers/mongodb/mongodb-query-parser.d.ts.map +1 -0
- package/esm/drivers/{mongo/mongo-query-parser.js → mongodb/mongodb-query-parser.js} +87 -87
- package/esm/drivers/mongodb/mongodb-query-parser.js.map +1 -0
- package/esm/drivers/{mongo/mongo-sync-adapter.d.ts → mongodb/mongodb-sync-adapter.d.ts} +2 -2
- package/esm/drivers/mongodb/mongodb-sync-adapter.d.ts.map +1 -0
- package/esm/drivers/{mongo/mongo-sync-adapter.js → mongodb/mongodb-sync-adapter.js} +2 -2
- package/esm/drivers/mongodb/mongodb-sync-adapter.js.map +1 -0
- package/{cjs/drivers/mongo → esm/drivers/mongodb}/types.d.ts +2 -2
- package/esm/drivers/mongodb/types.d.ts.map +1 -0
- package/esm/drivers/postgres/index.d.ts +16 -0
- package/esm/drivers/postgres/index.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-blueprint.d.ts +64 -0
- package/esm/drivers/postgres/postgres-blueprint.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-blueprint.js +121 -0
- package/esm/drivers/postgres/postgres-blueprint.js.map +1 -0
- package/esm/drivers/postgres/postgres-dialect.d.ts +135 -0
- package/esm/drivers/postgres/postgres-dialect.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-dialect.js +245 -0
- package/esm/drivers/postgres/postgres-dialect.js.map +1 -0
- package/esm/drivers/postgres/postgres-driver.d.ts +360 -0
- package/esm/drivers/postgres/postgres-driver.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-driver.js +763 -0
- package/esm/drivers/postgres/postgres-driver.js.map +1 -0
- package/esm/drivers/postgres/postgres-migration-driver.d.ts +297 -0
- package/esm/drivers/postgres/postgres-migration-driver.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-migration-driver.js +578 -0
- package/esm/drivers/postgres/postgres-migration-driver.js.map +1 -0
- package/esm/drivers/postgres/postgres-query-builder.d.ts +824 -0
- package/esm/drivers/postgres/postgres-query-builder.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-query-builder.js +1800 -0
- package/esm/drivers/postgres/postgres-query-builder.js.map +1 -0
- package/esm/drivers/postgres/postgres-query-parser.d.ts +308 -0
- package/esm/drivers/postgres/postgres-query-parser.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-query-parser.js +706 -0
- package/esm/drivers/postgres/postgres-query-parser.js.map +1 -0
- package/esm/drivers/postgres/postgres-sync-adapter.d.ts +83 -0
- package/esm/drivers/postgres/postgres-sync-adapter.d.ts.map +1 -0
- package/esm/drivers/postgres/postgres-sync-adapter.js +197 -0
- package/esm/drivers/postgres/postgres-sync-adapter.js.map +1 -0
- package/esm/drivers/postgres/types.d.ts +142 -0
- package/esm/drivers/postgres/types.d.ts.map +1 -0
- package/esm/drivers/sql/index.d.ts +10 -0
- package/esm/drivers/sql/index.d.ts.map +1 -0
- package/esm/drivers/sql/sql-dialect.contract.d.ts +203 -0
- package/esm/drivers/sql/sql-dialect.contract.d.ts.map +1 -0
- package/esm/drivers/sql/sql-types.d.ts +202 -0
- package/esm/drivers/sql/sql-types.d.ts.map +1 -0
- package/esm/index.d.ts +9 -6
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -1
- package/esm/migration/migration-runner.d.ts.map +1 -1
- package/esm/migration/migration-runner.js +3 -0
- package/esm/migration/migration-runner.js.map +1 -1
- package/esm/model/model.d.ts +236 -1
- package/esm/model/model.d.ts.map +1 -1
- package/esm/model/model.js +203 -4
- package/esm/model/model.js.map +1 -1
- package/esm/relations/helpers.d.ts +156 -0
- package/esm/relations/helpers.d.ts.map +1 -0
- package/esm/relations/helpers.js +197 -0
- package/esm/relations/helpers.js.map +1 -0
- package/esm/relations/index.d.ts +33 -0
- package/esm/relations/index.d.ts.map +1 -0
- package/esm/relations/pivot-operations.d.ts +160 -0
- package/esm/relations/pivot-operations.d.ts.map +1 -0
- package/esm/relations/pivot-operations.js +293 -0
- package/esm/relations/pivot-operations.js.map +1 -0
- package/esm/relations/relation-loader.d.ts +194 -0
- package/esm/relations/relation-loader.d.ts.map +1 -0
- package/esm/relations/relation-loader.js +466 -0
- package/esm/relations/relation-loader.js.map +1 -0
- package/esm/relations/types.d.ts +280 -0
- package/esm/relations/types.d.ts.map +1 -0
- package/esm/sync/model-sync-operation.js +1 -1
- package/esm/sync/model-sync-operation.js.map +1 -1
- package/esm/utils/connect-to-database.d.ts.map +1 -1
- package/esm/utils/connect-to-database.js +15 -3
- package/esm/utils/connect-to-database.js.map +1 -1
- package/esm/utils/define-model.d.ts +51 -29
- package/esm/utils/define-model.d.ts.map +1 -1
- package/esm/validation/rules/database-model-rule.js +1 -1
- package/esm/validation/rules/database-model-rule.js.map +1 -1
- package/package.json +4 -4
- package/cjs/drivers/mongo/mongo-id-generator.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongo-id-generator.js.map +0 -1
- package/cjs/drivers/mongo/mongo-migration-driver.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongo-migration-driver.js.map +0 -1
- package/cjs/drivers/mongo/mongo-query-builder.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongo-query-builder.js.map +0 -1
- package/cjs/drivers/mongo/mongo-query-operations.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongo-query-operations.js.map +0 -1
- package/cjs/drivers/mongo/mongo-query-parser.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongo-query-parser.js.map +0 -1
- package/cjs/drivers/mongo/mongo-sync-adapter.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongo-sync-adapter.js.map +0 -1
- package/cjs/drivers/mongo/mongodb-blueprint.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongodb-blueprint.js.map +0 -1
- package/cjs/drivers/mongo/mongodb-driver.d.ts.map +0 -1
- package/cjs/drivers/mongo/mongodb-driver.js.map +0 -1
- package/cjs/drivers/mongo/types.d.ts.map +0 -1
- package/esm/drivers/mongo/mongo-id-generator.d.ts.map +0 -1
- package/esm/drivers/mongo/mongo-id-generator.js.map +0 -1
- package/esm/drivers/mongo/mongo-migration-driver.d.ts.map +0 -1
- package/esm/drivers/mongo/mongo-migration-driver.js.map +0 -1
- package/esm/drivers/mongo/mongo-query-builder.d.ts.map +0 -1
- package/esm/drivers/mongo/mongo-query-builder.js.map +0 -1
- package/esm/drivers/mongo/mongo-query-operations.d.ts.map +0 -1
- package/esm/drivers/mongo/mongo-query-operations.js.map +0 -1
- package/esm/drivers/mongo/mongo-query-parser.d.ts.map +0 -1
- package/esm/drivers/mongo/mongo-query-parser.js.map +0 -1
- package/esm/drivers/mongo/mongo-sync-adapter.d.ts.map +0 -1
- package/esm/drivers/mongo/mongo-sync-adapter.js.map +0 -1
- package/esm/drivers/mongo/mongodb-blueprint.d.ts.map +0 -1
- package/esm/drivers/mongo/mongodb-blueprint.js.map +0 -1
- package/esm/drivers/mongo/mongodb-driver.d.ts.map +0 -1
- package/esm/drivers/mongo/mongodb-driver.js.map +0 -1
- package/esm/drivers/mongo/types.d.ts.map +0 -1
- /package/cjs/drivers/{mongo → mongodb}/mongodb-blueprint.d.ts +0 -0
- /package/cjs/drivers/{mongo → mongodb}/mongodb-blueprint.js +0 -0
- /package/esm/drivers/{mongo → mongodb}/mongodb-blueprint.d.ts +0 -0
- /package/esm/drivers/{mongo → mongodb}/mongodb-blueprint.js +0 -0
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
import {PostgresDialect}from'./postgres-dialect.js';/**
|
|
2
|
+
* PostgreSQL Query Parser
|
|
3
|
+
*
|
|
4
|
+
* Translates Cascade query operations into PostgreSQL SQL queries.
|
|
5
|
+
* Mirrors the MongoQueryParser pattern but generates SQL instead
|
|
6
|
+
* of MongoDB aggregation pipelines.
|
|
7
|
+
*
|
|
8
|
+
* @module cascade/drivers/postgres
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* PostgreSQL Query Parser.
|
|
12
|
+
*
|
|
13
|
+
* Converts a list of query operations into a SQL query string with parameters.
|
|
14
|
+
* Handles SELECT, WHERE, JOIN, ORDER BY, GROUP BY, LIMIT/OFFSET clauses.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const parser = new PostgresQueryParser({
|
|
19
|
+
* table: 'users',
|
|
20
|
+
* operations: [
|
|
21
|
+
* { type: 'where', data: { field: 'name', operator: '=', value: 'Alice' } },
|
|
22
|
+
* { type: 'orderBy', data: { field: 'createdAt', direction: 'desc' } },
|
|
23
|
+
* { type: 'limit', data: { value: 10 } }
|
|
24
|
+
* ]
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* const { sql, params } = parser.parse();
|
|
28
|
+
* // sql: 'SELECT * FROM "users" WHERE "name" = $1 ORDER BY "createdAt" DESC LIMIT 10'
|
|
29
|
+
* // params: ['Alice']
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
class PostgresQueryParser {
|
|
33
|
+
/**
|
|
34
|
+
* Target table name.
|
|
35
|
+
*/
|
|
36
|
+
table;
|
|
37
|
+
/**
|
|
38
|
+
* Table alias.
|
|
39
|
+
*/
|
|
40
|
+
alias;
|
|
41
|
+
/**
|
|
42
|
+
* Operations to process.
|
|
43
|
+
*/
|
|
44
|
+
operations;
|
|
45
|
+
/**
|
|
46
|
+
* SQL dialect for syntax.
|
|
47
|
+
*/
|
|
48
|
+
dialect;
|
|
49
|
+
/**
|
|
50
|
+
* Current parameter index (1-based for PostgreSQL).
|
|
51
|
+
*/
|
|
52
|
+
paramIndex = 1;
|
|
53
|
+
/**
|
|
54
|
+
* Collected parameters.
|
|
55
|
+
*/
|
|
56
|
+
params = [];
|
|
57
|
+
/**
|
|
58
|
+
* SELECT columns.
|
|
59
|
+
*/
|
|
60
|
+
selectColumns = [];
|
|
61
|
+
/**
|
|
62
|
+
* Deselected (excluded) columns.
|
|
63
|
+
*/
|
|
64
|
+
deselectColumns = [];
|
|
65
|
+
/**
|
|
66
|
+
* Raw SELECT expressions.
|
|
67
|
+
*/
|
|
68
|
+
selectRaw = [];
|
|
69
|
+
/**
|
|
70
|
+
* WHERE clauses.
|
|
71
|
+
*/
|
|
72
|
+
whereClauses = [];
|
|
73
|
+
/**
|
|
74
|
+
* JOIN clauses.
|
|
75
|
+
*/
|
|
76
|
+
joinClauses = [];
|
|
77
|
+
/**
|
|
78
|
+
* ORDER BY clauses.
|
|
79
|
+
*/
|
|
80
|
+
orderClauses = [];
|
|
81
|
+
/**
|
|
82
|
+
* GROUP BY columns.
|
|
83
|
+
*/
|
|
84
|
+
groupColumns = [];
|
|
85
|
+
/**
|
|
86
|
+
* HAVING clauses.
|
|
87
|
+
*/
|
|
88
|
+
havingClauses = [];
|
|
89
|
+
/**
|
|
90
|
+
* LIMIT value.
|
|
91
|
+
*/
|
|
92
|
+
limitValue;
|
|
93
|
+
/**
|
|
94
|
+
* OFFSET value.
|
|
95
|
+
*/
|
|
96
|
+
offsetValue;
|
|
97
|
+
/**
|
|
98
|
+
* DISTINCT flag.
|
|
99
|
+
*/
|
|
100
|
+
isDistinct = false;
|
|
101
|
+
/**
|
|
102
|
+
* Tracked joined tables (for table reference detection).
|
|
103
|
+
*/
|
|
104
|
+
joinedTables = new Set();
|
|
105
|
+
/**
|
|
106
|
+
* Create a new query parser.
|
|
107
|
+
*
|
|
108
|
+
* @param options - Parser configuration
|
|
109
|
+
*/
|
|
110
|
+
constructor(options) {
|
|
111
|
+
this.table = options.table;
|
|
112
|
+
this.alias = options.alias;
|
|
113
|
+
this.operations = options.operations;
|
|
114
|
+
this.dialect = options.dialect ?? new PostgresDialect();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Parse all operations and build the SQL query.
|
|
118
|
+
*
|
|
119
|
+
* @returns Object with SQL string and parameter values
|
|
120
|
+
*/
|
|
121
|
+
parse() {
|
|
122
|
+
// Process each operation
|
|
123
|
+
for (const operation of this.operations) {
|
|
124
|
+
this.processOperation(operation);
|
|
125
|
+
}
|
|
126
|
+
// Build the final SQL query
|
|
127
|
+
const sql = this.buildSql();
|
|
128
|
+
return { sql, params: this.params };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get a formatted string representation of the query.
|
|
132
|
+
*
|
|
133
|
+
* @returns Formatted SQL with parameters
|
|
134
|
+
*/
|
|
135
|
+
toPrettyString() {
|
|
136
|
+
const { sql, params } = this.parse();
|
|
137
|
+
return `${sql}\n-- Parameters: ${JSON.stringify(params)}`;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Process a single operation.
|
|
141
|
+
*
|
|
142
|
+
* @param operation - The operation to process
|
|
143
|
+
*/
|
|
144
|
+
processOperation(operation) {
|
|
145
|
+
const { type, data } = operation;
|
|
146
|
+
switch (type) {
|
|
147
|
+
// WHERE operations
|
|
148
|
+
case "where":
|
|
149
|
+
this.processWhere(data, "AND");
|
|
150
|
+
break;
|
|
151
|
+
case "orWhere":
|
|
152
|
+
this.processWhere(data, "OR");
|
|
153
|
+
break;
|
|
154
|
+
case "whereRaw":
|
|
155
|
+
this.processWhereRaw(data, "AND");
|
|
156
|
+
break;
|
|
157
|
+
case "orWhereRaw":
|
|
158
|
+
this.processWhereRaw(data, "OR");
|
|
159
|
+
break;
|
|
160
|
+
case "whereIn":
|
|
161
|
+
this.processWhereIn(data, false);
|
|
162
|
+
break;
|
|
163
|
+
case "whereNotIn":
|
|
164
|
+
this.processWhereIn(data, true);
|
|
165
|
+
break;
|
|
166
|
+
case "whereNull":
|
|
167
|
+
this.processWhereNull(data, false);
|
|
168
|
+
break;
|
|
169
|
+
case "whereNotNull":
|
|
170
|
+
this.processWhereNull(data, true);
|
|
171
|
+
break;
|
|
172
|
+
case "whereBetween":
|
|
173
|
+
this.processWhereBetween(data, false);
|
|
174
|
+
break;
|
|
175
|
+
case "whereNotBetween":
|
|
176
|
+
this.processWhereBetween(data, true);
|
|
177
|
+
break;
|
|
178
|
+
case "whereLike":
|
|
179
|
+
this.processWhereLike(data, false);
|
|
180
|
+
break;
|
|
181
|
+
case "whereNotLike":
|
|
182
|
+
this.processWhereLike(data, true);
|
|
183
|
+
break;
|
|
184
|
+
case "whereColumn":
|
|
185
|
+
this.processWhereColumn(data, "AND");
|
|
186
|
+
break;
|
|
187
|
+
case "orWhereColumn":
|
|
188
|
+
this.processWhereColumn(data, "OR");
|
|
189
|
+
break;
|
|
190
|
+
case "whereJsonContains":
|
|
191
|
+
this.processWhereJsonContains(data, false);
|
|
192
|
+
break;
|
|
193
|
+
case "whereJsonDoesntContain":
|
|
194
|
+
this.processWhereJsonContains(data, true);
|
|
195
|
+
break;
|
|
196
|
+
case "whereFullText":
|
|
197
|
+
this.processWhereFullText(data);
|
|
198
|
+
break;
|
|
199
|
+
// SELECT operations
|
|
200
|
+
case "select":
|
|
201
|
+
this.processSelect(data);
|
|
202
|
+
break;
|
|
203
|
+
case "selectRaw":
|
|
204
|
+
this.processSelectRaw(data);
|
|
205
|
+
break;
|
|
206
|
+
case "deselect":
|
|
207
|
+
this.processDeselect(data);
|
|
208
|
+
break;
|
|
209
|
+
// JOIN operations
|
|
210
|
+
case "join":
|
|
211
|
+
case "innerJoin":
|
|
212
|
+
this.processJoin(data, "INNER");
|
|
213
|
+
break;
|
|
214
|
+
case "leftJoin":
|
|
215
|
+
this.processJoin(data, "LEFT");
|
|
216
|
+
break;
|
|
217
|
+
case "rightJoin":
|
|
218
|
+
this.processJoin(data, "RIGHT");
|
|
219
|
+
break;
|
|
220
|
+
case "fullJoin":
|
|
221
|
+
this.processJoin(data, "FULL OUTER");
|
|
222
|
+
break;
|
|
223
|
+
case "crossJoin":
|
|
224
|
+
this.processCrossJoin(data);
|
|
225
|
+
break;
|
|
226
|
+
case "joinRaw":
|
|
227
|
+
this.processJoinRaw(data);
|
|
228
|
+
break;
|
|
229
|
+
// ORDER operations
|
|
230
|
+
case "orderBy":
|
|
231
|
+
this.processOrderBy(data);
|
|
232
|
+
break;
|
|
233
|
+
case "orderByRaw":
|
|
234
|
+
this.processOrderByRaw(data);
|
|
235
|
+
break;
|
|
236
|
+
// GROUP operations
|
|
237
|
+
case "groupBy":
|
|
238
|
+
this.processGroupBy(data);
|
|
239
|
+
break;
|
|
240
|
+
case "having":
|
|
241
|
+
this.processHaving(data);
|
|
242
|
+
break;
|
|
243
|
+
case "havingRaw":
|
|
244
|
+
this.processHavingRaw(data);
|
|
245
|
+
break;
|
|
246
|
+
// LIMIT operations
|
|
247
|
+
case "limit":
|
|
248
|
+
this.limitValue = data.value;
|
|
249
|
+
break;
|
|
250
|
+
case "offset":
|
|
251
|
+
this.offsetValue = data.value;
|
|
252
|
+
break;
|
|
253
|
+
// Other
|
|
254
|
+
case "distinct":
|
|
255
|
+
this.isDistinct = true;
|
|
256
|
+
break;
|
|
257
|
+
// joinWith select related columns
|
|
258
|
+
case "selectRelatedColumns":
|
|
259
|
+
this.processSelectRelatedColumns(data);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Build the final SQL query from collected clauses.
|
|
265
|
+
*
|
|
266
|
+
* @returns Complete SQL query string
|
|
267
|
+
*/
|
|
268
|
+
buildSql() {
|
|
269
|
+
const parts = [];
|
|
270
|
+
// SELECT clause
|
|
271
|
+
parts.push(this.buildSelectClause());
|
|
272
|
+
// FROM clause
|
|
273
|
+
const quotedTable = this.dialect.quoteIdentifier(this.table);
|
|
274
|
+
const fromClause = this.alias
|
|
275
|
+
? `FROM ${quotedTable} AS ${this.dialect.quoteIdentifier(this.alias)}`
|
|
276
|
+
: `FROM ${quotedTable}`;
|
|
277
|
+
parts.push(fromClause);
|
|
278
|
+
// JOIN clauses
|
|
279
|
+
if (this.joinClauses.length > 0) {
|
|
280
|
+
parts.push(this.joinClauses.join(" "));
|
|
281
|
+
}
|
|
282
|
+
// WHERE clause
|
|
283
|
+
if (this.whereClauses.length > 0) {
|
|
284
|
+
parts.push(`WHERE ${this.whereClauses.join(" ")}`);
|
|
285
|
+
}
|
|
286
|
+
// GROUP BY clause
|
|
287
|
+
if (this.groupColumns.length > 0) {
|
|
288
|
+
const quotedCols = this.groupColumns.map((c) => this.dialect.quoteIdentifier(c));
|
|
289
|
+
parts.push(`GROUP BY ${quotedCols.join(", ")}`);
|
|
290
|
+
}
|
|
291
|
+
// HAVING clause
|
|
292
|
+
if (this.havingClauses.length > 0) {
|
|
293
|
+
parts.push(`HAVING ${this.havingClauses.join(" AND ")}`);
|
|
294
|
+
}
|
|
295
|
+
// ORDER BY clause
|
|
296
|
+
if (this.orderClauses.length > 0) {
|
|
297
|
+
parts.push(`ORDER BY ${this.orderClauses.join(", ")}`);
|
|
298
|
+
}
|
|
299
|
+
// LIMIT/OFFSET
|
|
300
|
+
const limitOffset = this.dialect.limitOffset(this.limitValue, this.offsetValue);
|
|
301
|
+
if (limitOffset) {
|
|
302
|
+
parts.push(limitOffset);
|
|
303
|
+
}
|
|
304
|
+
return parts.join(" ");
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Build the SELECT clause.
|
|
308
|
+
*
|
|
309
|
+
* @returns SELECT clause string
|
|
310
|
+
*/
|
|
311
|
+
buildSelectClause() {
|
|
312
|
+
const distinct = this.isDistinct ? "DISTINCT " : "";
|
|
313
|
+
// If no specific columns, select all
|
|
314
|
+
if (this.selectColumns.length === 0 && this.selectRaw.length === 0) {
|
|
315
|
+
// Handle deselect by explicitly listing columns (would need schema info)
|
|
316
|
+
// For now, just use *
|
|
317
|
+
return `SELECT ${distinct}*`;
|
|
318
|
+
}
|
|
319
|
+
const columns = [];
|
|
320
|
+
// Add selected columns
|
|
321
|
+
for (const col of this.selectColumns) {
|
|
322
|
+
if (!this.deselectColumns.includes(col)) {
|
|
323
|
+
columns.push(this.dialect.quoteIdentifier(col));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// Add raw expressions
|
|
327
|
+
columns.push(...this.selectRaw);
|
|
328
|
+
return `SELECT ${distinct}${columns.join(", ")}`;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Add a placeholder and parameter.
|
|
332
|
+
*
|
|
333
|
+
* @param value - Parameter value
|
|
334
|
+
* @returns Placeholder string ($1, $2, etc.)
|
|
335
|
+
*/
|
|
336
|
+
addParam(value) {
|
|
337
|
+
this.params.push(value);
|
|
338
|
+
return this.dialect.placeholder(this.paramIndex++);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Process a basic WHERE operation.
|
|
342
|
+
*/
|
|
343
|
+
processWhere(data, boolean) {
|
|
344
|
+
const field = data.field;
|
|
345
|
+
const operator = data.operator ?? "=";
|
|
346
|
+
const value = data.value;
|
|
347
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
348
|
+
const placeholder = this.addParam(value);
|
|
349
|
+
const clause = `${quotedField} ${this.mapOperator(operator)} ${placeholder}`;
|
|
350
|
+
this.addWhereClause(clause, boolean);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Process a raw WHERE operation.
|
|
354
|
+
*/
|
|
355
|
+
processWhereRaw(data, boolean) {
|
|
356
|
+
const expression = data.expression;
|
|
357
|
+
const bindings = data.bindings ?? [];
|
|
358
|
+
// Replace ? placeholders with $n
|
|
359
|
+
let processed = expression;
|
|
360
|
+
for (const binding of bindings) {
|
|
361
|
+
processed = processed.replace("?", this.addParam(binding));
|
|
362
|
+
}
|
|
363
|
+
this.addWhereClause(processed, boolean);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Process WHERE IN / NOT IN.
|
|
367
|
+
*/
|
|
368
|
+
processWhereIn(data, negate) {
|
|
369
|
+
const field = data.field;
|
|
370
|
+
const values = data.values;
|
|
371
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
372
|
+
const operator = negate ? "!= ALL" : "= ANY";
|
|
373
|
+
const placeholder = this.addParam(values);
|
|
374
|
+
this.addWhereClause(`${quotedField} ${operator}(${placeholder})`, "AND");
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Process WHERE NULL / NOT NULL.
|
|
378
|
+
*/
|
|
379
|
+
processWhereNull(data, negate) {
|
|
380
|
+
const field = data.field;
|
|
381
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
382
|
+
const clause = negate ? `${quotedField} IS NOT NULL` : `${quotedField} IS NULL`;
|
|
383
|
+
this.addWhereClause(clause, "AND");
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Process WHERE BETWEEN / NOT BETWEEN.
|
|
387
|
+
*/
|
|
388
|
+
processWhereBetween(data, negate) {
|
|
389
|
+
const field = data.field;
|
|
390
|
+
const range = data.range;
|
|
391
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
392
|
+
const placeholder1 = this.addParam(range[0]);
|
|
393
|
+
const placeholder2 = this.addParam(range[1]);
|
|
394
|
+
const keyword = negate ? "NOT BETWEEN" : "BETWEEN";
|
|
395
|
+
this.addWhereClause(`${quotedField} ${keyword} ${placeholder1} AND ${placeholder2}`, "AND");
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Process WHERE LIKE / NOT LIKE.
|
|
399
|
+
*/
|
|
400
|
+
processWhereLike(data, negate) {
|
|
401
|
+
const field = data.field;
|
|
402
|
+
const pattern = data.pattern;
|
|
403
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
404
|
+
const { operator } = this.dialect.likePattern(pattern, true);
|
|
405
|
+
const placeholder = this.addParam(pattern);
|
|
406
|
+
const keyword = negate ? `NOT ${operator}` : operator;
|
|
407
|
+
this.addWhereClause(`${quotedField} ${keyword} ${placeholder}`, "AND");
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Process WHERE column comparison.
|
|
411
|
+
*/
|
|
412
|
+
processWhereColumn(data, boolean) {
|
|
413
|
+
const first = data.first;
|
|
414
|
+
const operator = data.operator ?? "=";
|
|
415
|
+
const second = data.second;
|
|
416
|
+
const quotedFirst = this.dialect.quoteIdentifier(first);
|
|
417
|
+
const quotedSecond = this.dialect.quoteIdentifier(second);
|
|
418
|
+
this.addWhereClause(`${quotedFirst} ${operator} ${quotedSecond}`, boolean);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Process WHERE JSON contains.
|
|
422
|
+
*/
|
|
423
|
+
processWhereJsonContains(data, negate) {
|
|
424
|
+
const path = data.path;
|
|
425
|
+
const value = data.value;
|
|
426
|
+
const quotedPath = this.dialect.quoteIdentifier(path);
|
|
427
|
+
const jsonValue = JSON.stringify(value);
|
|
428
|
+
const operator = negate ? "NOT @>" : "@>";
|
|
429
|
+
this.addWhereClause(`${quotedPath} ${operator} '${jsonValue}'::jsonb`, "AND");
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Process full-text search WHERE.
|
|
433
|
+
*/
|
|
434
|
+
processWhereFullText(data) {
|
|
435
|
+
const fields = data.fields;
|
|
436
|
+
const query = data.query;
|
|
437
|
+
// Build tsvector from fields
|
|
438
|
+
const tsVectors = fields
|
|
439
|
+
.map((f) => `to_tsvector('english', ${this.dialect.quoteIdentifier(f)})`)
|
|
440
|
+
.join(" || ");
|
|
441
|
+
const placeholder = this.addParam(query);
|
|
442
|
+
this.addWhereClause(`(${tsVectors}) @@ plainto_tsquery('english', ${placeholder})`, "AND");
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Process SELECT operation.
|
|
446
|
+
*/
|
|
447
|
+
processSelect(data) {
|
|
448
|
+
const fields = data.fields;
|
|
449
|
+
if (Array.isArray(fields)) {
|
|
450
|
+
this.selectColumns.push(...fields);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
// Handle aliases: { field: 'alias' }
|
|
454
|
+
for (const [field, alias] of Object.entries(fields)) {
|
|
455
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
456
|
+
const quotedAlias = this.dialect.quoteIdentifier(alias);
|
|
457
|
+
this.selectRaw.push(`${quotedField} AS ${quotedAlias}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Process raw SELECT expression.
|
|
463
|
+
*/
|
|
464
|
+
processSelectRaw(data) {
|
|
465
|
+
const expression = data.expression;
|
|
466
|
+
if (typeof expression === "string") {
|
|
467
|
+
this.selectRaw.push(expression);
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
// Handle object expressions (for compatibility)
|
|
471
|
+
for (const [alias, expr] of Object.entries(expression)) {
|
|
472
|
+
this.selectRaw.push(`${expr} AS ${this.dialect.quoteIdentifier(alias)}`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Process DESELECT operation.
|
|
478
|
+
*/
|
|
479
|
+
processDeselect(data) {
|
|
480
|
+
const fields = data.fields;
|
|
481
|
+
this.deselectColumns.push(...fields);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Process SELECT for related columns (joinWith).
|
|
485
|
+
* Uses row_to_json to package all related columns into a single JSON column.
|
|
486
|
+
* Also ensures main table columns are selected.
|
|
487
|
+
*/
|
|
488
|
+
processSelectRelatedColumns(data) {
|
|
489
|
+
const alias = data.alias;
|
|
490
|
+
const quotedAlias = this.dialect.quoteIdentifier(alias);
|
|
491
|
+
const quotedTable = this.dialect.quoteIdentifier(this.table);
|
|
492
|
+
// Ensure main table columns are selected (only add once)
|
|
493
|
+
if (!this.selectRaw.includes(`${quotedTable}.*`)) {
|
|
494
|
+
this.selectRaw.unshift(`${quotedTable}.*`);
|
|
495
|
+
}
|
|
496
|
+
// Use row_to_json to get all columns from the joined table as a single JSON object
|
|
497
|
+
// The column will be named like "_rel_author" and contain { id, name, email, ... }
|
|
498
|
+
this.selectRaw.push(`row_to_json(${quotedAlias}.*) AS ${quotedAlias}`);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Process JOIN operation with smart field detection.
|
|
502
|
+
*
|
|
503
|
+
* Handles both regular columns and JSONB nested paths:
|
|
504
|
+
* - "id" → "table"."id" (auto-prefixed)
|
|
505
|
+
* - "users.id" → "users"."id" (explicit table)
|
|
506
|
+
* - "createdBy.id" → "table"."createdBy"->>'id' (JSONB path)
|
|
507
|
+
* - "posts.createdBy.id" → "posts"."createdBy"->>'id' (JSONB with table)
|
|
508
|
+
*/
|
|
509
|
+
processJoin(data, type) {
|
|
510
|
+
const options = data;
|
|
511
|
+
const joinTable = "table" in options ? options.table : "";
|
|
512
|
+
const localField = "localField" in options ? options.localField : "";
|
|
513
|
+
const foreignField = "foreignField" in options ? options.foreignField : "";
|
|
514
|
+
const alias = "alias" in options ? options.alias : undefined;
|
|
515
|
+
const quotedTable = this.dialect.quoteIdentifier(joinTable);
|
|
516
|
+
const tableRef = alias
|
|
517
|
+
? `${quotedTable} AS ${this.dialect.quoteIdentifier(alias)}`
|
|
518
|
+
: quotedTable;
|
|
519
|
+
const tableAlias = alias ?? joinTable;
|
|
520
|
+
// Track the joined table for reference detection
|
|
521
|
+
this.joinedTables.add(joinTable);
|
|
522
|
+
if (alias) {
|
|
523
|
+
this.joinedTables.add(alias);
|
|
524
|
+
}
|
|
525
|
+
// Parse local field (belongs to main table)
|
|
526
|
+
const quotedLocal = this.parseJoinField(localField, this.table, this.alias);
|
|
527
|
+
// Parse foreign field (belongs to join table)
|
|
528
|
+
const quotedForeign = this.parseJoinField(foreignField, joinTable, tableAlias);
|
|
529
|
+
this.joinClauses.push(`${type} JOIN ${tableRef} ON ${quotedLocal} = ${quotedForeign}`);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Parse a join field with smart detection for table prefixes and JSONB paths.
|
|
533
|
+
*
|
|
534
|
+
* @param field - The field string (e.g., "id", "users.id", "createdBy.id")
|
|
535
|
+
* @param defaultTable - Default table to use if no prefix
|
|
536
|
+
* @param tableAlias - Table alias to use if provided
|
|
537
|
+
* @returns Properly quoted SQL expression
|
|
538
|
+
*/
|
|
539
|
+
parseJoinField(field, defaultTable, tableAlias) {
|
|
540
|
+
if (!field)
|
|
541
|
+
return "";
|
|
542
|
+
const effectiveTable = tableAlias ?? defaultTable;
|
|
543
|
+
const parts = field.split(".");
|
|
544
|
+
// Single part: just a column name, prefix with default table
|
|
545
|
+
if (parts.length === 1) {
|
|
546
|
+
return `${this.dialect.quoteIdentifier(effectiveTable)}.${this.dialect.quoteIdentifier(field)}`;
|
|
547
|
+
}
|
|
548
|
+
// Two parts: could be "table.column" or "jsonbColumn.key"
|
|
549
|
+
if (parts.length === 2) {
|
|
550
|
+
const [first, second] = parts;
|
|
551
|
+
// Check if first part is a known table (main table, join table, or alias)
|
|
552
|
+
if (this.isTableReference(first)) {
|
|
553
|
+
// It's table.column - regular column reference
|
|
554
|
+
return `${this.dialect.quoteIdentifier(first)}.${this.dialect.quoteIdentifier(second)}`;
|
|
555
|
+
}
|
|
556
|
+
// It's jsonbColumn.key - JSONB path
|
|
557
|
+
return this.buildJsonbPath(effectiveTable, first, [second]);
|
|
558
|
+
}
|
|
559
|
+
// Three or more parts: "table.jsonbColumn.key..." or "jsonbColumn.key1.key2..."
|
|
560
|
+
const [first, second, ...rest] = parts;
|
|
561
|
+
if (this.isTableReference(first)) {
|
|
562
|
+
// First part is table: "posts.createdBy.id" → table is "posts", JSONB is "createdBy.id"
|
|
563
|
+
return this.buildJsonbPath(first, second, rest);
|
|
564
|
+
}
|
|
565
|
+
// No table prefix: "createdBy.address.city" → use default table
|
|
566
|
+
return this.buildJsonbPath(effectiveTable, first, [second, ...rest]);
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Check if a string is a table reference (main table or join table).
|
|
570
|
+
*/
|
|
571
|
+
isTableReference(name) {
|
|
572
|
+
if (name === this.table || name === this.alias) {
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
if (this.joinedTables.has(name)) {
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Build a JSONB path expression.
|
|
582
|
+
*
|
|
583
|
+
* @param table - Table name
|
|
584
|
+
* @param column - JSONB column name
|
|
585
|
+
* @param path - Array of nested keys
|
|
586
|
+
* @returns PostgreSQL JSONB path expression
|
|
587
|
+
*
|
|
588
|
+
* @example
|
|
589
|
+
* buildJsonbPath("posts", "createdBy", ["id"])
|
|
590
|
+
* // Returns: ("posts"."createdBy"->>'id')::integer
|
|
591
|
+
*
|
|
592
|
+
* buildJsonbPath("posts", "createdBy", ["address", "city"])
|
|
593
|
+
* // Returns: "posts"."createdBy"->'address'->>'city'
|
|
594
|
+
*/
|
|
595
|
+
buildJsonbPath(table, column, path) {
|
|
596
|
+
const quotedTable = this.dialect.quoteIdentifier(table);
|
|
597
|
+
const quotedColumn = this.dialect.quoteIdentifier(column);
|
|
598
|
+
if (path.length === 0) {
|
|
599
|
+
return `${quotedTable}.${quotedColumn}`;
|
|
600
|
+
}
|
|
601
|
+
// Build JSONB path: use -> for intermediate keys, ->> for final key (returns text)
|
|
602
|
+
let expression = `${quotedTable}.${quotedColumn}`;
|
|
603
|
+
for (let i = 0; i < path.length; i++) {
|
|
604
|
+
const isLast = i === path.length - 1;
|
|
605
|
+
const operator = isLast ? "->>" : "->";
|
|
606
|
+
expression += `${operator}'${path[i]}'`;
|
|
607
|
+
}
|
|
608
|
+
// Cast to integer if the path ends with 'id' (common pattern for foreign keys)
|
|
609
|
+
const lastKey = path[path.length - 1].toLowerCase();
|
|
610
|
+
if (lastKey === "id" || lastKey.endsWith("id")) {
|
|
611
|
+
expression = `(${expression})::integer`;
|
|
612
|
+
}
|
|
613
|
+
return expression;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Process CROSS JOIN operation.
|
|
617
|
+
*/
|
|
618
|
+
processCrossJoin(data) {
|
|
619
|
+
const table = data.table;
|
|
620
|
+
const quotedTable = this.dialect.quoteIdentifier(table);
|
|
621
|
+
this.joinClauses.push(`CROSS JOIN ${quotedTable}`);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Process raw JOIN expression.
|
|
625
|
+
*/
|
|
626
|
+
processJoinRaw(data) {
|
|
627
|
+
const expression = data.expression;
|
|
628
|
+
const bindings = data.bindings ?? [];
|
|
629
|
+
let processed = expression;
|
|
630
|
+
for (const binding of bindings) {
|
|
631
|
+
processed = processed.replace("?", this.addParam(binding));
|
|
632
|
+
}
|
|
633
|
+
this.joinClauses.push(processed);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Process ORDER BY operation.
|
|
637
|
+
*/
|
|
638
|
+
processOrderBy(data) {
|
|
639
|
+
const field = data.field;
|
|
640
|
+
const direction = (data.direction ?? "asc").toUpperCase();
|
|
641
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
642
|
+
this.orderClauses.push(`${quotedField} ${direction}`);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Process raw ORDER BY expression.
|
|
646
|
+
*/
|
|
647
|
+
processOrderByRaw(data) {
|
|
648
|
+
const expression = data.expression;
|
|
649
|
+
this.orderClauses.push(expression);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Process GROUP BY operation.
|
|
653
|
+
*/
|
|
654
|
+
processGroupBy(data) {
|
|
655
|
+
const fields = data.fields;
|
|
656
|
+
const columns = Array.isArray(fields) ? fields : [fields];
|
|
657
|
+
this.groupColumns.push(...columns);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Process HAVING operation.
|
|
661
|
+
*/
|
|
662
|
+
processHaving(data) {
|
|
663
|
+
const field = data.field;
|
|
664
|
+
const operator = data.operator ?? "=";
|
|
665
|
+
const value = data.value;
|
|
666
|
+
const quotedField = this.dialect.quoteIdentifier(field);
|
|
667
|
+
const placeholder = this.addParam(value);
|
|
668
|
+
this.havingClauses.push(`${quotedField} ${operator} ${placeholder}`);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Process raw HAVING expression.
|
|
672
|
+
*/
|
|
673
|
+
processHavingRaw(data) {
|
|
674
|
+
const expression = data.expression;
|
|
675
|
+
this.havingClauses.push(expression);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Add a WHERE clause with boolean operator.
|
|
679
|
+
*/
|
|
680
|
+
addWhereClause(clause, boolean) {
|
|
681
|
+
if (this.whereClauses.length === 0) {
|
|
682
|
+
this.whereClauses.push(clause);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
this.whereClauses.push(`${boolean} ${clause}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Map Cascade operators to SQL operators.
|
|
690
|
+
*/
|
|
691
|
+
mapOperator(operator) {
|
|
692
|
+
const mapping = {
|
|
693
|
+
"=": "=",
|
|
694
|
+
"!=": "!=",
|
|
695
|
+
"<>": "<>",
|
|
696
|
+
">": ">",
|
|
697
|
+
">=": ">=",
|
|
698
|
+
"<": "<",
|
|
699
|
+
"<=": "<=",
|
|
700
|
+
like: "LIKE",
|
|
701
|
+
notLike: "NOT LIKE",
|
|
702
|
+
ilike: "ILIKE",
|
|
703
|
+
};
|
|
704
|
+
return mapping[operator.toLowerCase()] ?? operator;
|
|
705
|
+
}
|
|
706
|
+
}export{PostgresQueryParser};//# sourceMappingURL=postgres-query-parser.js.map
|