ts-prorm-orm 1.1.0
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/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed window-function builder API.
|
|
4
|
+
*
|
|
5
|
+
* Provides a fluent, chainable builder for SQL window functions
|
|
6
|
+
* (`OVER (PARTITION BY ... ORDER BY ... ROWS/RANGE BETWEEN ...)`) so callers
|
|
7
|
+
* never need to hand-write raw SQL strings for `ROW_NUMBER()`, `RANK()`,
|
|
8
|
+
* `LAG()`, `LEAD()`, and friends.
|
|
9
|
+
*
|
|
10
|
+
* This module is intentionally self-contained: it does not add any new
|
|
11
|
+
* dispatch branches to the shared SQL compiler. Every builder resolves
|
|
12
|
+
* directly to a final SQL fragment string, which is then wrapped with the
|
|
13
|
+
* existing `literalExpr()`/`literal()` helpers from `src/operators.ts` so it
|
|
14
|
+
* slots into `attributes: [...]` arrays exactly the way `fn()`/`col()`/
|
|
15
|
+
* `cast()` do today (see `attributeToSql()` / `processAttribute()` in
|
|
16
|
+
* `src/query-builders/sql-compiler.ts`, which both already know how to
|
|
17
|
+
* render `{ __type: 'literal', sql }` and `[expr, alias]` tuples).
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { rowNumber, rank, lag, sumOver } from './query-builders/functions/window';
|
|
22
|
+
* import { col } from '../../operators';
|
|
23
|
+
*
|
|
24
|
+
* User.findAll({
|
|
25
|
+
* attributes: [
|
|
26
|
+
* 'department',
|
|
27
|
+
* 'salary',
|
|
28
|
+
* rowNumber().partitionBy('department').orderBy('salary', 'DESC').as('rn'),
|
|
29
|
+
* rank().partitionBy(col('department')).orderBy(col('salary'), 'DESC').as('rnk'),
|
|
30
|
+
* lag(col('salary'), 1).partitionBy('department').orderBy('hireDate').as('prevSalary'),
|
|
31
|
+
* sumOver(col('salary'))
|
|
32
|
+
* .partitionBy('department')
|
|
33
|
+
* .orderBy('hireDate')
|
|
34
|
+
* .rows({ start: 'unboundedPreceding', end: 'currentRow' })
|
|
35
|
+
* .as('runningTotal'),
|
|
36
|
+
* ],
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.WindowFunctionBuilder = exports.NullsOrder = void 0;
|
|
42
|
+
exports.rowNumber = rowNumber;
|
|
43
|
+
exports.rank = rank;
|
|
44
|
+
exports.denseRank = denseRank;
|
|
45
|
+
exports.percentRank = percentRank;
|
|
46
|
+
exports.cumeDist = cumeDist;
|
|
47
|
+
exports.ntile = ntile;
|
|
48
|
+
exports.lag = lag;
|
|
49
|
+
exports.lead = lead;
|
|
50
|
+
exports.firstValue = firstValue;
|
|
51
|
+
exports.lastValue = lastValue;
|
|
52
|
+
exports.nthValue = nthValue;
|
|
53
|
+
exports.sumOver = sumOver;
|
|
54
|
+
exports.avgOver = avgOver;
|
|
55
|
+
exports.countOver = countOver;
|
|
56
|
+
exports.minOver = minOver;
|
|
57
|
+
exports.maxOver = maxOver;
|
|
58
|
+
exports.windowFn = windowFn;
|
|
59
|
+
const operators_1 = require("../../operators");
|
|
60
|
+
/**
|
|
61
|
+
* `NULLS FIRST` / `NULLS LAST` modifier, supported by most (not all) dialects.
|
|
62
|
+
*
|
|
63
|
+
* Companion enum {@link NullsOrder} is available for the `'FIRST' | 'LAST'`
|
|
64
|
+
* string-literal union below; raw string literals continue to work unchanged.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Using raw string literals (still supported)
|
|
69
|
+
* rowNumber().orderBy('salary', 'DESC', 'LAST')
|
|
70
|
+
*
|
|
71
|
+
* // Using the NullsOrder enum
|
|
72
|
+
* import { NullsOrder } from './query-builders/functions/window';
|
|
73
|
+
* rowNumber().orderBy('salary', 'DESC', NullsOrder.LAST)
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
var NullsOrder;
|
|
77
|
+
(function (NullsOrder) {
|
|
78
|
+
NullsOrder["FIRST"] = "FIRST";
|
|
79
|
+
NullsOrder["LAST"] = "LAST";
|
|
80
|
+
})(NullsOrder || (exports.NullsOrder = NullsOrder = {}));
|
|
81
|
+
function escapeIdentifier(name) {
|
|
82
|
+
// Support dotted "table.column" references, same convention as col().
|
|
83
|
+
if (name.includes('.')) {
|
|
84
|
+
return name
|
|
85
|
+
.split('.')
|
|
86
|
+
.map((part) => `"${part.replace(/"/g, '""')}"`)
|
|
87
|
+
.join('.');
|
|
88
|
+
}
|
|
89
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Flatten a window-function argument (string, ColExpression, FnExpression,
|
|
93
|
+
* LiteralExpression, number, null) into a raw SQL fragment string.
|
|
94
|
+
*/
|
|
95
|
+
function toSqlFragment(arg) {
|
|
96
|
+
if (arg === null)
|
|
97
|
+
return 'NULL';
|
|
98
|
+
if (arg === undefined)
|
|
99
|
+
return 'NULL';
|
|
100
|
+
if (typeof arg === 'number') {
|
|
101
|
+
return String(arg);
|
|
102
|
+
}
|
|
103
|
+
if (typeof arg === 'string') {
|
|
104
|
+
return escapeIdentifier(arg);
|
|
105
|
+
}
|
|
106
|
+
if ((0, operators_1.isFnExpression)(arg)) {
|
|
107
|
+
const args = arg.args.map((a) => toSqlFragment(a)).join(', ');
|
|
108
|
+
return `${arg.fn}(${args})`;
|
|
109
|
+
}
|
|
110
|
+
if ((0, operators_1.isColExpression)(arg)) {
|
|
111
|
+
return escapeIdentifier(arg.col);
|
|
112
|
+
}
|
|
113
|
+
if ((0, operators_1.isLiteralExpression)(arg)) {
|
|
114
|
+
// Handles both { __type: 'literal', sql } and { $literal: sql } shapes.
|
|
115
|
+
return arg.sql ?? arg.$literal;
|
|
116
|
+
}
|
|
117
|
+
// Fallback: stringify.
|
|
118
|
+
return String(arg);
|
|
119
|
+
}
|
|
120
|
+
function frameBoundaryToSql(boundary) {
|
|
121
|
+
if (boundary === 'unboundedPreceding')
|
|
122
|
+
return 'UNBOUNDED PRECEDING';
|
|
123
|
+
if (boundary === 'unboundedFollowing')
|
|
124
|
+
return 'UNBOUNDED FOLLOWING';
|
|
125
|
+
if (boundary === 'currentRow')
|
|
126
|
+
return 'CURRENT ROW';
|
|
127
|
+
if (typeof boundary === 'object' && boundary !== null) {
|
|
128
|
+
if ('preceding' in boundary)
|
|
129
|
+
return `${boundary.preceding} PRECEDING`;
|
|
130
|
+
if ('following' in boundary)
|
|
131
|
+
return `${boundary.following} FOLLOWING`;
|
|
132
|
+
}
|
|
133
|
+
throw new Error(`Invalid window frame boundary: ${JSON.stringify(boundary)}`);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* A chainable builder that accumulates PARTITION BY / ORDER BY / frame
|
|
137
|
+
* clauses for a single window function call and renders the final
|
|
138
|
+
* `FN(args) OVER (...)` SQL fragment.
|
|
139
|
+
*/
|
|
140
|
+
class WindowFunctionBuilder {
|
|
141
|
+
constructor(fnName, fnArgs = []) {
|
|
142
|
+
this.partitions = [];
|
|
143
|
+
this.orders = [];
|
|
144
|
+
this.fnName = fnName;
|
|
145
|
+
this.fnArgs = fnArgs;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Set (or narrow) the target dialect. Only matters for the rare cases
|
|
149
|
+
* where OVER-clause syntax genuinely diverges (all listed dialects use the
|
|
150
|
+
* same ANSI-standard OVER syntax today, so this is mostly a documented
|
|
151
|
+
* escape hatch for future dialect-specific tweaks).
|
|
152
|
+
*/
|
|
153
|
+
forDialect(dialect) {
|
|
154
|
+
this.dialect = dialect;
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
/** Add one or more PARTITION BY columns/expressions. Repeatable/additive. */
|
|
158
|
+
partitionBy(...cols) {
|
|
159
|
+
this.partitions.push(...cols);
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Add an ORDER BY entry to the window definition. Call multiple times to
|
|
164
|
+
* order by multiple columns; each call appends one entry.
|
|
165
|
+
*/
|
|
166
|
+
orderBy(expr, direction, nulls) {
|
|
167
|
+
this.orders.push({ expr, direction, nulls });
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
/** Set a ROWS frame clause, e.g. `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`. */
|
|
171
|
+
rows(frame) {
|
|
172
|
+
this.frameMode = 'ROWS';
|
|
173
|
+
this.frame = frame;
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
/** Set a RANGE frame clause, e.g. `RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`. */
|
|
177
|
+
range(frame) {
|
|
178
|
+
this.frameMode = 'RANGE';
|
|
179
|
+
this.frame = frame;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
/** Render the `FN(args) OVER (...)` SQL fragment (no alias). */
|
|
183
|
+
toSQL() {
|
|
184
|
+
const argsSql = this.fnArgs.map((a) => toSqlFragment(a)).join(', ');
|
|
185
|
+
const overParts = [];
|
|
186
|
+
if (this.partitions.length > 0) {
|
|
187
|
+
overParts.push(`PARTITION BY ${this.partitions.map((p) => toSqlFragment(p)).join(', ')}`);
|
|
188
|
+
}
|
|
189
|
+
if (this.orders.length > 0) {
|
|
190
|
+
const orderSql = this.orders
|
|
191
|
+
.map((o) => {
|
|
192
|
+
let part = toSqlFragment(o.expr);
|
|
193
|
+
if (o.direction)
|
|
194
|
+
part += ` ${o.direction}`;
|
|
195
|
+
if (o.nulls)
|
|
196
|
+
part += ` NULLS ${o.nulls}`;
|
|
197
|
+
return part;
|
|
198
|
+
})
|
|
199
|
+
.join(', ');
|
|
200
|
+
overParts.push(`ORDER BY ${orderSql}`);
|
|
201
|
+
}
|
|
202
|
+
if (this.frameMode && this.frame) {
|
|
203
|
+
const start = frameBoundaryToSql(this.frame.start);
|
|
204
|
+
if (this.frame.end) {
|
|
205
|
+
const end = frameBoundaryToSql(this.frame.end);
|
|
206
|
+
overParts.push(`${this.frameMode} BETWEEN ${start} AND ${end}`);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
overParts.push(`${this.frameMode} ${start}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const overClause = overParts.length > 0 ? `OVER (${overParts.join(' ')})` : 'OVER ()';
|
|
213
|
+
return `${this.fnName}(${argsSql}) ${overClause}`;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Terminal call: returns an `[expr, alias]` tuple compatible with the
|
|
217
|
+
* existing `attributes: [...]` pattern (see `attributeToSql()` /
|
|
218
|
+
* `attributeToSqlWithAlias()` in `sql-compiler.ts`, both of which already
|
|
219
|
+
* know how to render `[LiteralExpression, alias]` tuples without any core
|
|
220
|
+
* changes).
|
|
221
|
+
*/
|
|
222
|
+
as(alias) {
|
|
223
|
+
return [(0, operators_1.literalExpr)(this.toSQL()), alias];
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Allow the builder to be used directly wherever a raw SQL literal is
|
|
227
|
+
* expected (e.g. passed straight into `literal()`-consuming positions)
|
|
228
|
+
* without requiring `.as(...)`.
|
|
229
|
+
*/
|
|
230
|
+
toLiteral() {
|
|
231
|
+
return (0, operators_1.literalExpr)(this.toSQL());
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.WindowFunctionBuilder = WindowFunctionBuilder;
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
// Ranking functions
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
/** `ROW_NUMBER() OVER (...)` */
|
|
239
|
+
function rowNumber() {
|
|
240
|
+
return new WindowFunctionBuilder('ROW_NUMBER');
|
|
241
|
+
}
|
|
242
|
+
/** `RANK() OVER (...)` */
|
|
243
|
+
function rank() {
|
|
244
|
+
return new WindowFunctionBuilder('RANK');
|
|
245
|
+
}
|
|
246
|
+
/** `DENSE_RANK() OVER (...)` */
|
|
247
|
+
function denseRank() {
|
|
248
|
+
return new WindowFunctionBuilder('DENSE_RANK');
|
|
249
|
+
}
|
|
250
|
+
/** `PERCENT_RANK() OVER (...)` */
|
|
251
|
+
function percentRank() {
|
|
252
|
+
return new WindowFunctionBuilder('PERCENT_RANK');
|
|
253
|
+
}
|
|
254
|
+
/** `CUME_DIST() OVER (...)` */
|
|
255
|
+
function cumeDist() {
|
|
256
|
+
return new WindowFunctionBuilder('CUME_DIST');
|
|
257
|
+
}
|
|
258
|
+
/** `NTILE(buckets) OVER (...)` */
|
|
259
|
+
function ntile(buckets) {
|
|
260
|
+
return new WindowFunctionBuilder('NTILE', [buckets]);
|
|
261
|
+
}
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
// Value / offset functions
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
/** `LAG(expr[, offset[, default]]) OVER (...)` */
|
|
266
|
+
function lag(expr, offset, defaultValue) {
|
|
267
|
+
const args = [expr];
|
|
268
|
+
if (offset !== undefined)
|
|
269
|
+
args.push(offset);
|
|
270
|
+
if (defaultValue !== undefined)
|
|
271
|
+
args.push(defaultValue);
|
|
272
|
+
return new WindowFunctionBuilder('LAG', args);
|
|
273
|
+
}
|
|
274
|
+
/** `LEAD(expr[, offset[, default]]) OVER (...)` */
|
|
275
|
+
function lead(expr, offset, defaultValue) {
|
|
276
|
+
const args = [expr];
|
|
277
|
+
if (offset !== undefined)
|
|
278
|
+
args.push(offset);
|
|
279
|
+
if (defaultValue !== undefined)
|
|
280
|
+
args.push(defaultValue);
|
|
281
|
+
return new WindowFunctionBuilder('LEAD', args);
|
|
282
|
+
}
|
|
283
|
+
/** `FIRST_VALUE(expr) OVER (...)` */
|
|
284
|
+
function firstValue(expr) {
|
|
285
|
+
return new WindowFunctionBuilder('FIRST_VALUE', [expr]);
|
|
286
|
+
}
|
|
287
|
+
/** `LAST_VALUE(expr) OVER (...)` */
|
|
288
|
+
function lastValue(expr) {
|
|
289
|
+
return new WindowFunctionBuilder('LAST_VALUE', [expr]);
|
|
290
|
+
}
|
|
291
|
+
/** `NTH_VALUE(expr, n) OVER (...)` */
|
|
292
|
+
function nthValue(expr, n) {
|
|
293
|
+
return new WindowFunctionBuilder('NTH_VALUE', [expr, n]);
|
|
294
|
+
}
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
// Window-aggregate forms
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
/** `SUM(expr) OVER (...)` */
|
|
299
|
+
function sumOver(expr) {
|
|
300
|
+
return new WindowFunctionBuilder('SUM', [expr]);
|
|
301
|
+
}
|
|
302
|
+
/** `AVG(expr) OVER (...)` */
|
|
303
|
+
function avgOver(expr) {
|
|
304
|
+
return new WindowFunctionBuilder('AVG', [expr]);
|
|
305
|
+
}
|
|
306
|
+
/** `COUNT(expr | *) OVER (...)` */
|
|
307
|
+
function countOver(expr) {
|
|
308
|
+
if (expr === undefined) {
|
|
309
|
+
return new WindowFunctionBuilder('COUNT', [(0, operators_1.literalExpr)('*')]);
|
|
310
|
+
}
|
|
311
|
+
return new WindowFunctionBuilder('COUNT', [expr]);
|
|
312
|
+
}
|
|
313
|
+
/** `MIN(expr) OVER (...)` */
|
|
314
|
+
function minOver(expr) {
|
|
315
|
+
return new WindowFunctionBuilder('MIN', [expr]);
|
|
316
|
+
}
|
|
317
|
+
/** `MAX(expr) OVER (...)` */
|
|
318
|
+
function maxOver(expr) {
|
|
319
|
+
return new WindowFunctionBuilder('MAX', [expr]);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Generic escape hatch: build a window function call for any function name
|
|
323
|
+
* not covered by the named helpers above (e.g. dialect-specific analytic
|
|
324
|
+
* functions).
|
|
325
|
+
*/
|
|
326
|
+
function windowFn(fnName, ...args) {
|
|
327
|
+
return new WindowFunctionBuilder(fnName.toUpperCase(), args);
|
|
328
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Include builder for the ORM
|
|
4
|
+
* Handles JOIN clauses for model associations
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.IncludeBuilder = void 0;
|
|
8
|
+
exports.createIncludeBuilder = createIncludeBuilder;
|
|
9
|
+
exports.buildIncludes = buildIncludes;
|
|
10
|
+
const where_builder_1 = require("./where-builder");
|
|
11
|
+
/**
|
|
12
|
+
* IncludeBuilder class for building JOIN clauses
|
|
13
|
+
*/
|
|
14
|
+
class IncludeBuilder {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a nested WhereBuilder (sharing replacements/model/dialect) used to
|
|
17
|
+
* render `on`, `where`, and `lateralSubquery.where` clauses on included tables
|
|
18
|
+
*/
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
this.whereBuilder = new where_builder_1.WhereBuilder({
|
|
22
|
+
replacements: options.replacements,
|
|
23
|
+
model: options.model,
|
|
24
|
+
dialect: options.dialect,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build JOIN clauses from include options
|
|
29
|
+
*/
|
|
30
|
+
build(includes) {
|
|
31
|
+
const sqlParts = [];
|
|
32
|
+
const values = [];
|
|
33
|
+
for (const include of includes) {
|
|
34
|
+
const result = this.buildInclude(include, values);
|
|
35
|
+
if (result) {
|
|
36
|
+
sqlParts.push(result);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { sql: sqlParts.join(' '), values };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a single include (can handle nested includes)
|
|
43
|
+
*/
|
|
44
|
+
buildInclude(include, values) {
|
|
45
|
+
// Determine model and alias
|
|
46
|
+
const model = include.model;
|
|
47
|
+
const tableName = model?.tableName || model?.name || String(model);
|
|
48
|
+
const alias = include.as || tableName;
|
|
49
|
+
// Get schema from include options (supports joining across schemas)
|
|
50
|
+
const schema = include.schema;
|
|
51
|
+
const schemaDelimiter = this.options.schemaDelimiter || '.';
|
|
52
|
+
// Build table name with schema if specified (e.g., "schema.tableName")
|
|
53
|
+
const fullTableName = schema ? `${schema}${schemaDelimiter}${tableName}` : tableName;
|
|
54
|
+
// Handle anti-join (antiJoin or exclude option)
|
|
55
|
+
const isAntiJoin = include.antiJoin === true || include.exclude === true;
|
|
56
|
+
// Get anti-join type (defaults to leftJoinIsNull)
|
|
57
|
+
const antiJoinType = include.antiJoinType || 'leftJoinIsNull';
|
|
58
|
+
// For notExists and notIn, we handle differently - return null from join and handle in where
|
|
59
|
+
if (isAntiJoin && (antiJoinType === 'notExists' || antiJoinType === 'notIn')) {
|
|
60
|
+
// Return empty string - these will be handled in the where clause with subqueries
|
|
61
|
+
return '';
|
|
62
|
+
}
|
|
63
|
+
// Determine join type
|
|
64
|
+
let joinType = this.getJoinType(include);
|
|
65
|
+
// For anti-joins with leftJoinIsNull, always use LEFT JOIN
|
|
66
|
+
if (isAntiJoin && antiJoinType === 'leftJoinIsNull') {
|
|
67
|
+
joinType = 'LEFT JOIN';
|
|
68
|
+
}
|
|
69
|
+
// Build ON clause
|
|
70
|
+
const onClause = this.buildOnClause(include, fullTableName, alias, values);
|
|
71
|
+
// Build the JOIN clause
|
|
72
|
+
let sql = `${joinType} ${this.escapeId(fullTableName)} ${this.escapeId(alias)}`;
|
|
73
|
+
// Handle CROSS APPLY / OUTER APPLY (SQL Server)
|
|
74
|
+
if (include.apply) {
|
|
75
|
+
const applyType = include.apply === 'CROSS' ? 'CROSS APPLY' : 'OUTER APPLY';
|
|
76
|
+
sql = `${applyType} ${this.escapeId(fullTableName)} ${this.escapeId(alias)}`;
|
|
77
|
+
}
|
|
78
|
+
// Handle LATERAL keyword (PostgreSQL only)
|
|
79
|
+
if (include.lateral) {
|
|
80
|
+
sql = `${joinType} LATERAL ${this.escapeId(fullTableName)} ${this.escapeId(alias)}`;
|
|
81
|
+
}
|
|
82
|
+
// Handle lateralSubquery (inline subquery)
|
|
83
|
+
if (include.lateralSubquery) {
|
|
84
|
+
const subqueryModel = include.lateralSubquery.model;
|
|
85
|
+
const subqueryTableName = subqueryModel?.tableName || subqueryModel?.name || 'subquery';
|
|
86
|
+
const subqueryAlias = alias;
|
|
87
|
+
// Build SELECT clause
|
|
88
|
+
let selectColumns = '*';
|
|
89
|
+
if (include.lateralSubquery.attributes) {
|
|
90
|
+
if (Array.isArray(include.lateralSubquery.attributes)) {
|
|
91
|
+
selectColumns = include.lateralSubquery.attributes
|
|
92
|
+
.map((c) => this.escapeId(c))
|
|
93
|
+
.join(', ');
|
|
94
|
+
}
|
|
95
|
+
else if (include.lateralSubquery.attributes.include) {
|
|
96
|
+
selectColumns = include.lateralSubquery.attributes.include
|
|
97
|
+
.map((c) => this.escapeId(c))
|
|
98
|
+
.join(', ');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Build the lateral subquery
|
|
102
|
+
let subquery = `SELECT ${selectColumns} FROM ${this.escapeId(subqueryTableName)}`;
|
|
103
|
+
// Add ON clause if there's a foreign key relationship
|
|
104
|
+
if (onClause) {
|
|
105
|
+
subquery += ` ON ${onClause}`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const fk = include.foreignKey;
|
|
109
|
+
const foreignKey = typeof fk === 'string'
|
|
110
|
+
? fk
|
|
111
|
+
: Array.isArray(fk)
|
|
112
|
+
? fk[0]
|
|
113
|
+
: fk?.name || this.guessForeignKey(include, tableName);
|
|
114
|
+
const parentAlias = this.options.parentAlias || 'main';
|
|
115
|
+
subquery += ` ON ${this.escapeId(parentAlias)}.${this.escapeId(foreignKey)} = ${this.escapeId(subqueryAlias)}.${this.getPrimaryKey(include)}`;
|
|
116
|
+
}
|
|
117
|
+
// Add WHERE clause if specified
|
|
118
|
+
if (include.lateralSubquery.where) {
|
|
119
|
+
const whereResult = this.whereBuilder.build(include.lateralSubquery.where);
|
|
120
|
+
if (whereResult.sql) {
|
|
121
|
+
subquery += ` AND (${whereResult.sql})`;
|
|
122
|
+
values.push(...whereResult.values);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
sql = `${joinType} LATERAL (${subquery}) ${this.escapeId(subqueryAlias)}`;
|
|
126
|
+
}
|
|
127
|
+
if (onClause) {
|
|
128
|
+
sql += ` ON ${onClause}`;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// Fallback: use foreign key if not specified
|
|
132
|
+
const fk = include.foreignKey;
|
|
133
|
+
const foreignKey = typeof fk === 'string'
|
|
134
|
+
? fk
|
|
135
|
+
: Array.isArray(fk)
|
|
136
|
+
? fk[0]
|
|
137
|
+
: fk?.name || this.guessForeignKey(include, tableName);
|
|
138
|
+
const parentAlias = this.options.parentAlias || 'main';
|
|
139
|
+
sql += ` ON ${this.escapeId(parentAlias)}.${this.escapeId(foreignKey)} = ${this.escapeId(alias)}.${this.escapeId(this.getPrimaryKey(include))}`;
|
|
140
|
+
}
|
|
141
|
+
// Handle through table for many-to-many associations
|
|
142
|
+
if (include.through) {
|
|
143
|
+
sql = this.buildThroughJoin(include, sql, values);
|
|
144
|
+
}
|
|
145
|
+
// Handle where clause on the joined table
|
|
146
|
+
if (include.where) {
|
|
147
|
+
const whereResult = this.whereBuilder.build(include.where);
|
|
148
|
+
if (whereResult.sql) {
|
|
149
|
+
sql += ` AND (${whereResult.sql})`;
|
|
150
|
+
values.push(...whereResult.values);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// For anti-joins, add WHERE clause to check for NULL (LEFT JOIN WHERE NULL pattern)
|
|
154
|
+
if (isAntiJoin) {
|
|
155
|
+
const pk = this.getPrimaryKey(include);
|
|
156
|
+
sql += ` AND ${this.escapeId(alias)}.${this.escapeId(pk)} IS NULL`;
|
|
157
|
+
}
|
|
158
|
+
// Handle nested includes
|
|
159
|
+
if (include.include && include.include.length > 0) {
|
|
160
|
+
const nestedBuilder = new IncludeBuilder({
|
|
161
|
+
...this.options,
|
|
162
|
+
parentAlias: alias,
|
|
163
|
+
});
|
|
164
|
+
for (const nestedInclude of include.include) {
|
|
165
|
+
const nestedResult = nestedBuilder.buildInclude(nestedInclude, values);
|
|
166
|
+
if (nestedResult) {
|
|
167
|
+
sql += ` ${nestedResult}`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return sql;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Build a through-table join for many-to-many associations
|
|
175
|
+
*/
|
|
176
|
+
buildThroughJoin(include, baseJoin, values) {
|
|
177
|
+
const through = include.through;
|
|
178
|
+
const throughModel = through?.model;
|
|
179
|
+
const throughTableName = throughModel?.tableName || throughModel?.name || String(through?.model || 'through');
|
|
180
|
+
const throughAlias = through?.as || `${throughTableName}_through`;
|
|
181
|
+
// Build the join through the through table
|
|
182
|
+
const parentAlias = this.options.parentAlias || 'main';
|
|
183
|
+
// Use the provided 'as' alias or fall back to table name
|
|
184
|
+
const targetAlias = include.as || targetTableName(include.model);
|
|
185
|
+
// We need to modify the base join to go through the through table
|
|
186
|
+
// This creates: INNER JOIN through_table ON parent.id = through_table.parent_id
|
|
187
|
+
// INNER JOIN target_table ON through_table.target_id = target_table.id
|
|
188
|
+
const parentKey = this.options.parentKey || 'id';
|
|
189
|
+
const childKey = this.options.childKey || this.getPrimaryKey(include);
|
|
190
|
+
const throughJoin = `${this.getJoinType(include)} ${this.escapeId(throughTableName)} ${this.escapeId(throughAlias)} ON ${this.escapeId(parentAlias)}.${this.escapeId(parentKey)} = ${this.escapeId(throughAlias)}.${this.escapeId(this.guessForeignKey(include, parentAlias))}`;
|
|
191
|
+
return `${this.getJoinType(include)} ${this.escapeId(throughTableName)} ${this.escapeId(throughAlias)} ON ${this.escapeId(parentAlias)}.${this.escapeId(parentKey)} = ${this.escapeId(throughAlias)}.${this.guessForeignKey(include, parentAlias)} ${this.getJoinType(include)} ${this.escapeId(targetTableName(include.model))} ${this.escapeId(targetAlias)} ON ${this.escapeId(throughAlias)}.${this.escapeId(childKey)} = ${this.escapeId(targetAlias)}.${this.escapeId(childKey)}`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get the join type based on include options
|
|
195
|
+
*/
|
|
196
|
+
getJoinType(include) {
|
|
197
|
+
// Check for explicit joinType first
|
|
198
|
+
if (include.joinType) {
|
|
199
|
+
switch (include.joinType) {
|
|
200
|
+
case 'INNER':
|
|
201
|
+
return 'INNER JOIN';
|
|
202
|
+
case 'LEFT':
|
|
203
|
+
return 'LEFT JOIN';
|
|
204
|
+
case 'RIGHT':
|
|
205
|
+
return 'RIGHT JOIN';
|
|
206
|
+
case 'FULL':
|
|
207
|
+
return 'FULL OUTER JOIN';
|
|
208
|
+
case 'CROSS':
|
|
209
|
+
return 'CROSS JOIN';
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Fall back to required flag
|
|
213
|
+
if (include.required) {
|
|
214
|
+
return 'INNER JOIN';
|
|
215
|
+
}
|
|
216
|
+
// Default to LEFT JOIN for optional associations
|
|
217
|
+
return 'LEFT JOIN';
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Build the ON clause for a join
|
|
221
|
+
*/
|
|
222
|
+
buildOnClause(include, tableName, alias, values) {
|
|
223
|
+
// If explicit ON clause is provided
|
|
224
|
+
if (include.on) {
|
|
225
|
+
const whereResult = this.whereBuilder.build(include.on);
|
|
226
|
+
// Need to handle the ON clause differently - it uses the alias
|
|
227
|
+
return this.buildOnFromWhere(include.on, alias, values);
|
|
228
|
+
}
|
|
229
|
+
// Otherwise, try to build from association
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Build ON clause from WhereOptions
|
|
234
|
+
*/
|
|
235
|
+
buildOnFromWhere(on, alias, values) {
|
|
236
|
+
const onConditions = [];
|
|
237
|
+
if (typeof on !== 'object') {
|
|
238
|
+
return '';
|
|
239
|
+
}
|
|
240
|
+
for (const [key, value] of Object.entries(on)) {
|
|
241
|
+
if (key.startsWith('$'))
|
|
242
|
+
continue;
|
|
243
|
+
if (typeof value === 'object' && value !== null) {
|
|
244
|
+
const valueObj = value;
|
|
245
|
+
// Handle $eq
|
|
246
|
+
if (valueObj.$eq !== undefined) {
|
|
247
|
+
values.push(valueObj.$eq);
|
|
248
|
+
onConditions.push(`${alias}.${this.escapeId(key)} = ?`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
values.push(value);
|
|
253
|
+
onConditions.push(`${alias}.${this.escapeId(key)} = ?`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return onConditions.join(' AND ');
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Guess the foreign key name for an association
|
|
260
|
+
*/
|
|
261
|
+
guessForeignKey(include, tableName) {
|
|
262
|
+
// If foreignKey is explicitly provided
|
|
263
|
+
if (include.foreignKey) {
|
|
264
|
+
return typeof include.foreignKey === 'string'
|
|
265
|
+
? include.foreignKey
|
|
266
|
+
: include.foreignKey.name || 'id';
|
|
267
|
+
}
|
|
268
|
+
// Default convention: tableName_id
|
|
269
|
+
const model = include.model;
|
|
270
|
+
const modelName = model?.name || tableName;
|
|
271
|
+
return `${modelName.charAt(0).toLowerCase() + modelName.slice(1)}Id`;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get the primary key of a model
|
|
275
|
+
*/
|
|
276
|
+
getPrimaryKey(include) {
|
|
277
|
+
const model = include.model;
|
|
278
|
+
// Try to get from rawAttributes
|
|
279
|
+
if (model?.rawAttributes) {
|
|
280
|
+
for (const [attrName, attr] of Object.entries(model.rawAttributes)) {
|
|
281
|
+
if (attr.primaryKey) {
|
|
282
|
+
return attrName;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Default to 'id'
|
|
287
|
+
return 'id';
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Escape an identifier
|
|
291
|
+
*/
|
|
292
|
+
escapeId(identifier) {
|
|
293
|
+
const id = String(identifier ?? '');
|
|
294
|
+
if (this.options.dialect?.escapeId) {
|
|
295
|
+
return this.options.dialect.escapeId(id);
|
|
296
|
+
}
|
|
297
|
+
return `"${id.replace(/"/g, '""')}"`;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
exports.IncludeBuilder = IncludeBuilder;
|
|
301
|
+
/**
|
|
302
|
+
* Helper to get table name from model
|
|
303
|
+
*/
|
|
304
|
+
function targetTableName(model) {
|
|
305
|
+
if (typeof model === 'string') {
|
|
306
|
+
return model;
|
|
307
|
+
}
|
|
308
|
+
return model.tableName || model.name || String(model);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Create an IncludeBuilder instance
|
|
312
|
+
*/
|
|
313
|
+
function createIncludeBuilder(options) {
|
|
314
|
+
return new IncludeBuilder(options);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Quick helper to build include JOINs
|
|
318
|
+
*/
|
|
319
|
+
function buildIncludes(includes, options) {
|
|
320
|
+
const builder = new IncludeBuilder(options);
|
|
321
|
+
return builder.build(includes);
|
|
322
|
+
}
|
|
323
|
+
exports.default = IncludeBuilder;
|