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,503 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed CASE WHEN ... THEN ... ELSE ... END expression builder.
|
|
4
|
+
*
|
|
5
|
+
* `CASE WHEN` is ANSI-standard SQL (SQL-92) and is supported identically,
|
|
6
|
+
* with no syntax variation, across every dialect this ORM targets:
|
|
7
|
+
* - mysql, mariadb: full support (searched + simple CASE)
|
|
8
|
+
* - postgres, cockroachdb, redshift: full support (Postgres-derived)
|
|
9
|
+
* - mssql: full support (T-SQL CASE is ANSI-compliant)
|
|
10
|
+
* - oracle: full support (in addition to the older DECODE() function,
|
|
11
|
+
* Oracle has supported standard CASE since 9i)
|
|
12
|
+
* - sqlite: full support
|
|
13
|
+
* - db2: full support
|
|
14
|
+
* - snowflake: full support
|
|
15
|
+
* - clickhouse: full support, including both the "searched" form
|
|
16
|
+
* (`CASE WHEN cond THEN a END`) and the "simple" form
|
|
17
|
+
* (`CASE expr WHEN val THEN a END`). ClickHouse also offers the
|
|
18
|
+
* `multiIf()`/`if()` functions as engine-native alternatives, but plain
|
|
19
|
+
* `CASE` works exactly as ANSI SQL specifies, so no dialect branching is
|
|
20
|
+
* required here (unlike window frame extensions or JSON path functions).
|
|
21
|
+
*
|
|
22
|
+
* This module is intentionally self-contained: it does not add any new
|
|
23
|
+
* dispatch branches to the shared SQL compiler (`src/query-builders/sql-compiler.ts`)
|
|
24
|
+
* or to `src/operators.ts`. Every builder resolves directly to a final SQL
|
|
25
|
+
* fragment string, which is then wrapped with the existing `literalExpr()`
|
|
26
|
+
* helper from `src/operators.ts` so it slots into `attributes: [...]` arrays
|
|
27
|
+
* exactly the way `fn()`/`col()`/`cast()`/the window-function builder do
|
|
28
|
+
* today (see `attributeToSql()` in `sql-compiler.ts`, which already knows
|
|
29
|
+
* how to render `{ __type: 'literal', sql }` and `[expr, alias]` tuples).
|
|
30
|
+
*
|
|
31
|
+
* Condition syntax: `.when()` on the searched-CASE builder (`caseWhen()`)
|
|
32
|
+
* accepts the SAME condition-object shape already used by `where:` clauses
|
|
33
|
+
* throughout this ORM (see `src/operators.ts`'s `Op` symbols and
|
|
34
|
+
* `src/query-builders/where-builder.ts`), e.g.:
|
|
35
|
+
*
|
|
36
|
+
* { age: { [Op.gte]: 18 } }
|
|
37
|
+
* { [Op.and]: [{ age: { [Op.gte]: 18 } }, { status: 'active' }] }
|
|
38
|
+
* { [Op.between]: [...] } // via a field key, see below
|
|
39
|
+
*
|
|
40
|
+
* rather than inventing a new mini-language. This keeps `caseWhen().when(...)`
|
|
41
|
+
* consistent with `Model.findAll({ where: ... })` conditions. A shorthand
|
|
42
|
+
* 3-arg form, `.when(column, operator, value, thenValue)`, is also supported
|
|
43
|
+
* for ergonomic simple comparisons (mirroring `Op.where(col, op, value)`).
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { caseWhen, caseOf } from './query-builders/functions/conditional';
|
|
48
|
+
* import { Op, col } from '../../operators';
|
|
49
|
+
*
|
|
50
|
+
* // Searched CASE (condition-object form, same shape as `where:`)
|
|
51
|
+
* User.findAll({
|
|
52
|
+
* attributes: [
|
|
53
|
+
* 'name',
|
|
54
|
+
* caseWhen()
|
|
55
|
+
* .when({ age: { [Op.gte]: 18 } }, 'adult')
|
|
56
|
+
* .when({ age: { [Op.gte]: 13 } }, 'teen')
|
|
57
|
+
* .else('child')
|
|
58
|
+
* .as('ageGroup'),
|
|
59
|
+
* ],
|
|
60
|
+
* });
|
|
61
|
+
*
|
|
62
|
+
* // Searched CASE (3-arg shorthand form)
|
|
63
|
+
* caseWhen().when('age', '>=', 18, 'adult').else('minor').as('ageGroup');
|
|
64
|
+
*
|
|
65
|
+
* // Simple CASE (CASE expr WHEN val THEN result ... END)
|
|
66
|
+
* caseOf(col('status'))
|
|
67
|
+
* .when('active', 1)
|
|
68
|
+
* .when('inactive', 0)
|
|
69
|
+
* .else(null)
|
|
70
|
+
* .as('statusFlag');
|
|
71
|
+
*
|
|
72
|
+
* // Nested CASE
|
|
73
|
+
* caseWhen()
|
|
74
|
+
* .when({ age: { [Op.gte]: 65 } }, 'senior')
|
|
75
|
+
* .else(caseWhen().when({ age: { [Op.gte]: 18 } }, 'adult').else('minor'))
|
|
76
|
+
* .as('category');
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
80
|
+
exports.SimpleCaseBuilder = exports.CaseBuilder = void 0;
|
|
81
|
+
exports.caseWhen = caseWhen;
|
|
82
|
+
exports.caseOf = caseOf;
|
|
83
|
+
const operators_1 = require("../../operators");
|
|
84
|
+
function escapeIdentifier(name) {
|
|
85
|
+
if (name.includes('.')) {
|
|
86
|
+
return name
|
|
87
|
+
.split('.')
|
|
88
|
+
.map((part) => `"${part.replace(/"/g, '""')}"`)
|
|
89
|
+
.join('.');
|
|
90
|
+
}
|
|
91
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
92
|
+
}
|
|
93
|
+
function escapeStringLiteral(value) {
|
|
94
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Render a THEN/ELSE/simple-CASE value into a SQL fragment. Distinguishes
|
|
98
|
+
* plain value literals (strings get quoted/escaped, numbers/booleans are
|
|
99
|
+
* inlined, `null` becomes SQL NULL) from typed expressions (`col()`, `fn()`,
|
|
100
|
+
* `literal()`) and from nested CASE builders, which get rendered recursively.
|
|
101
|
+
*/
|
|
102
|
+
function toSqlFragment(value) {
|
|
103
|
+
if (value === null)
|
|
104
|
+
return 'NULL';
|
|
105
|
+
if (value instanceof CaseBuilder || value instanceof SimpleCaseBuilder) {
|
|
106
|
+
return value.toSQL();
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === 'number') {
|
|
109
|
+
return String(value);
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === 'boolean') {
|
|
112
|
+
return value ? 'TRUE' : 'FALSE';
|
|
113
|
+
}
|
|
114
|
+
if (typeof value === 'string') {
|
|
115
|
+
return escapeStringLiteral(value);
|
|
116
|
+
}
|
|
117
|
+
if ((0, operators_1.isFnExpression)(value)) {
|
|
118
|
+
const args = value.args.map((a) => toSqlFragment(a)).join(', ');
|
|
119
|
+
return `${value.fn}(${args})`;
|
|
120
|
+
}
|
|
121
|
+
if ((0, operators_1.isColExpression)(value)) {
|
|
122
|
+
return escapeIdentifier(value.col);
|
|
123
|
+
}
|
|
124
|
+
if ((0, operators_1.isLiteralExpression)(value)) {
|
|
125
|
+
return value.sql ?? value.$literal;
|
|
126
|
+
}
|
|
127
|
+
if ((0, operators_1.isColumnReference)(value)) {
|
|
128
|
+
return escapeIdentifier(value.$col);
|
|
129
|
+
}
|
|
130
|
+
// Fallback: stringify.
|
|
131
|
+
return String(value);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Render a "condition target" — i.e. an argument that is either a plain
|
|
135
|
+
* value or a column reference — used when comparing a field to a value
|
|
136
|
+
* inside a condition operator (e.g. the right-hand side of `$gt`, or the
|
|
137
|
+
* two ends of a `$between`). Reuses `toSqlFragment` since the value shapes
|
|
138
|
+
* (`$col`, `$literal`, plain values) are the same.
|
|
139
|
+
*/
|
|
140
|
+
function toComparisonSql(value) {
|
|
141
|
+
return toSqlFragment(value);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Compile a `where:`-shaped condition object into a raw (inline, non-
|
|
145
|
+
* parameterized) SQL boolean expression. This is a small, self-contained
|
|
146
|
+
* subset of the logic in `src/query-builders/where-builder.ts`'s
|
|
147
|
+
* `WhereBuilder`, adapted to produce inline SQL (values are escaped
|
|
148
|
+
* directly into the string) rather than `?` placeholders + a values array,
|
|
149
|
+
* since CASE expressions are embedded as raw SQL literals via
|
|
150
|
+
* `literalExpr()` rather than run through the parameterized query pipeline.
|
|
151
|
+
*
|
|
152
|
+
* Reuses `operatorToWhereKey()` and `isColumnReference()` from
|
|
153
|
+
* `src/operators.ts` (both already exported) instead of re-implementing the
|
|
154
|
+
* Symbol -> `$op` string mapping.
|
|
155
|
+
*/
|
|
156
|
+
function compileCondition(condition) {
|
|
157
|
+
if (condition === null || condition === undefined) {
|
|
158
|
+
return '1=1';
|
|
159
|
+
}
|
|
160
|
+
if (typeof condition !== 'object') {
|
|
161
|
+
// A bare scalar condition doesn't make sense without a field; treat as
|
|
162
|
+
// a raw truthy/falsy literal for robustness.
|
|
163
|
+
return condition ? '1=1' : '1=0';
|
|
164
|
+
}
|
|
165
|
+
const cond = condition;
|
|
166
|
+
// Symbol-based Op.and / Op.or / Op.not
|
|
167
|
+
const symbolKeys = Object.getOwnPropertySymbols(cond);
|
|
168
|
+
for (const sym of symbolKeys) {
|
|
169
|
+
if (sym === operators_1.Op.and) {
|
|
170
|
+
const parts = cond[sym].map((c) => `(${compileCondition(c)})`);
|
|
171
|
+
return parts.join(' AND ');
|
|
172
|
+
}
|
|
173
|
+
if (sym === operators_1.Op.or) {
|
|
174
|
+
const parts = cond[sym].map((c) => `(${compileCondition(c)})`);
|
|
175
|
+
return parts.join(' OR ');
|
|
176
|
+
}
|
|
177
|
+
if (sym === operators_1.Op.not) {
|
|
178
|
+
return `NOT (${compileCondition(cond[sym])})`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// String-based $and / $or / $not (same convention as WhereBuilder)
|
|
182
|
+
if ('$and' in cond) {
|
|
183
|
+
const parts = cond.$and.map((c) => `(${compileCondition(c)})`);
|
|
184
|
+
return parts.join(' AND ');
|
|
185
|
+
}
|
|
186
|
+
if ('$or' in cond) {
|
|
187
|
+
const parts = cond.$or.map((c) => `(${compileCondition(c)})`);
|
|
188
|
+
return parts.join(' OR ');
|
|
189
|
+
}
|
|
190
|
+
if ('$not' in cond) {
|
|
191
|
+
return `NOT (${compileCondition(cond.$not)})`;
|
|
192
|
+
}
|
|
193
|
+
// $where: { $col, $op: value } explicit column-reference condition
|
|
194
|
+
if ('$where' in cond) {
|
|
195
|
+
const whereClause = cond.$where;
|
|
196
|
+
const fieldSql = escapeIdentifier(whereClause.$col);
|
|
197
|
+
const opEntries = Object.entries(whereClause).filter(([k]) => k !== '$col');
|
|
198
|
+
const parts = opEntries.map(([opKey, val]) => compileFieldOperator(fieldSql, opKey, val));
|
|
199
|
+
return parts.join(' AND ') || fieldSql;
|
|
200
|
+
}
|
|
201
|
+
// Plain field conditions: { field: value } or { field: { [Op.x]: value } }
|
|
202
|
+
const fieldConditions = [];
|
|
203
|
+
for (const [field, value] of Object.entries(cond)) {
|
|
204
|
+
const fieldSql = escapeIdentifier(field);
|
|
205
|
+
if (value === null || value === undefined) {
|
|
206
|
+
fieldConditions.push(`${fieldSql} IS NULL`);
|
|
207
|
+
}
|
|
208
|
+
else if (typeof value === 'object' && !Array.isArray(value)) {
|
|
209
|
+
// Could contain Symbol operator keys (Op.gt, etc.) or string $op keys.
|
|
210
|
+
const opSymbolKeys = Object.getOwnPropertySymbols(value);
|
|
211
|
+
let handled = false;
|
|
212
|
+
for (const sym of opSymbolKeys) {
|
|
213
|
+
const opKey = (0, operators_1.operatorToWhereKey)(sym);
|
|
214
|
+
fieldConditions.push(compileFieldOperator(fieldSql, opKey, value[sym]));
|
|
215
|
+
handled = true;
|
|
216
|
+
}
|
|
217
|
+
for (const [opKey, opVal] of Object.entries(value)) {
|
|
218
|
+
fieldConditions.push(compileFieldOperator(fieldSql, opKey, opVal));
|
|
219
|
+
handled = true;
|
|
220
|
+
}
|
|
221
|
+
if (!handled) {
|
|
222
|
+
fieldConditions.push(`${fieldSql} IS NULL`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
fieldConditions.push(`${fieldSql} = ${toComparisonSql(value)}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return fieldConditions.join(' AND ');
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Compile a single `$op: value` pair (e.g. `$gt`, `$between`, `$isNull`)
|
|
233
|
+
* into a raw SQL fragment for the given already-escaped field SQL.
|
|
234
|
+
*/
|
|
235
|
+
function compileFieldOperator(fieldSql, opKey, value) {
|
|
236
|
+
switch (opKey) {
|
|
237
|
+
case '$eq':
|
|
238
|
+
return `${fieldSql} = ${toComparisonSql(value)}`;
|
|
239
|
+
case '$ne':
|
|
240
|
+
return `${fieldSql} != ${toComparisonSql(value)}`;
|
|
241
|
+
case '$gt':
|
|
242
|
+
return `${fieldSql} > ${toComparisonSql(value)}`;
|
|
243
|
+
case '$gte':
|
|
244
|
+
return `${fieldSql} >= ${toComparisonSql(value)}`;
|
|
245
|
+
case '$lt':
|
|
246
|
+
return `${fieldSql} < ${toComparisonSql(value)}`;
|
|
247
|
+
case '$lte':
|
|
248
|
+
return `${fieldSql} <= ${toComparisonSql(value)}`;
|
|
249
|
+
case '$like':
|
|
250
|
+
return `${fieldSql} LIKE ${toComparisonSql(value)}`;
|
|
251
|
+
case '$notLike':
|
|
252
|
+
return `${fieldSql} NOT LIKE ${toComparisonSql(value)}`;
|
|
253
|
+
case '$iLike':
|
|
254
|
+
return `${fieldSql} ILIKE ${toComparisonSql(value)}`;
|
|
255
|
+
case '$notILike':
|
|
256
|
+
return `${fieldSql} NOT ILIKE ${toComparisonSql(value)}`;
|
|
257
|
+
case '$col':
|
|
258
|
+
return `${fieldSql} = ${escapeIdentifier(String(value))}`;
|
|
259
|
+
case '$in': {
|
|
260
|
+
const values = Array.isArray(value) ? value : [];
|
|
261
|
+
if (values.length === 0)
|
|
262
|
+
return '1=0';
|
|
263
|
+
return `${fieldSql} IN (${values.map((v) => toComparisonSql(v)).join(', ')})`;
|
|
264
|
+
}
|
|
265
|
+
case '$notIn': {
|
|
266
|
+
const values = Array.isArray(value) ? value : [];
|
|
267
|
+
if (values.length === 0)
|
|
268
|
+
return '1=1';
|
|
269
|
+
return `${fieldSql} NOT IN (${values.map((v) => toComparisonSql(v)).join(', ')})`;
|
|
270
|
+
}
|
|
271
|
+
case '$between': {
|
|
272
|
+
const [start, end] = value;
|
|
273
|
+
return `${fieldSql} BETWEEN ${toComparisonSql(start)} AND ${toComparisonSql(end)}`;
|
|
274
|
+
}
|
|
275
|
+
case '$notBetween': {
|
|
276
|
+
const [start, end] = value;
|
|
277
|
+
return `${fieldSql} NOT BETWEEN ${toComparisonSql(start)} AND ${toComparisonSql(end)}`;
|
|
278
|
+
}
|
|
279
|
+
case '$isNull':
|
|
280
|
+
return value ? `${fieldSql} IS NULL` : `${fieldSql} IS NOT NULL`;
|
|
281
|
+
case '$isNotNull':
|
|
282
|
+
return value ? `${fieldSql} IS NOT NULL` : `${fieldSql} IS NULL`;
|
|
283
|
+
case '$regexp':
|
|
284
|
+
return `${fieldSql} REGEXP ${toComparisonSql(value)}`;
|
|
285
|
+
case '$notRegexp':
|
|
286
|
+
return `${fieldSql} NOT REGEXP ${toComparisonSql(value)}`;
|
|
287
|
+
case '$startsWith':
|
|
288
|
+
return `${fieldSql} LIKE ${escapeStringLiteral(`${value}%`)}`;
|
|
289
|
+
case '$notStartsWith':
|
|
290
|
+
return `${fieldSql} NOT LIKE ${escapeStringLiteral(`${value}%`)}`;
|
|
291
|
+
case '$endsWith':
|
|
292
|
+
return `${fieldSql} LIKE ${escapeStringLiteral(`%${value}`)}`;
|
|
293
|
+
case '$notEndsWith':
|
|
294
|
+
return `${fieldSql} NOT LIKE ${escapeStringLiteral(`%${value}`)}`;
|
|
295
|
+
case '$substring':
|
|
296
|
+
return `${fieldSql} LIKE ${escapeStringLiteral(`%${value}%`)}`;
|
|
297
|
+
case '$notSubstring':
|
|
298
|
+
return `${fieldSql} NOT LIKE ${escapeStringLiteral(`%${value}%`)}`;
|
|
299
|
+
default:
|
|
300
|
+
// Unknown operator: fall back to equality against the raw value to
|
|
301
|
+
// avoid silently dropping the condition.
|
|
302
|
+
return `${fieldSql} = ${toComparisonSql(value)}`;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Map a string comparison operator (e.g. `'>='`, `'!='`) or an `Op.*`
|
|
307
|
+
* Symbol to the `$op` key used internally, reusing `operatorToWhereKey()`
|
|
308
|
+
* for the Symbol case (same convention as `Op.where()`).
|
|
309
|
+
*/
|
|
310
|
+
function normalizeOperator(operator) {
|
|
311
|
+
if (typeof operator === 'symbol') {
|
|
312
|
+
return (0, operators_1.operatorToWhereKey)(operator);
|
|
313
|
+
}
|
|
314
|
+
switch (operator) {
|
|
315
|
+
case '=':
|
|
316
|
+
case '==':
|
|
317
|
+
return '$eq';
|
|
318
|
+
case '!=':
|
|
319
|
+
case '<>':
|
|
320
|
+
return '$ne';
|
|
321
|
+
case '>':
|
|
322
|
+
return '$gt';
|
|
323
|
+
case '>=':
|
|
324
|
+
return '$gte';
|
|
325
|
+
case '<':
|
|
326
|
+
return '$lt';
|
|
327
|
+
case '<=':
|
|
328
|
+
return '$lte';
|
|
329
|
+
case 'like':
|
|
330
|
+
return '$like';
|
|
331
|
+
case 'not like':
|
|
332
|
+
case 'not_like':
|
|
333
|
+
return '$notLike';
|
|
334
|
+
case 'in':
|
|
335
|
+
return '$in';
|
|
336
|
+
case 'not in':
|
|
337
|
+
case 'not_in':
|
|
338
|
+
return '$notIn';
|
|
339
|
+
case 'between':
|
|
340
|
+
return '$between';
|
|
341
|
+
case 'not between':
|
|
342
|
+
case 'not_between':
|
|
343
|
+
return '$notBetween';
|
|
344
|
+
case 'is null':
|
|
345
|
+
case 'is_null':
|
|
346
|
+
return '$isNull';
|
|
347
|
+
case 'is not null':
|
|
348
|
+
case 'is_not_null':
|
|
349
|
+
return '$isNotNull';
|
|
350
|
+
default:
|
|
351
|
+
return `$${operator}`;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Fluent builder for a "searched" CASE expression:
|
|
356
|
+
* CASE WHEN cond1 THEN result1 WHEN cond2 THEN result2 ... ELSE else END
|
|
357
|
+
*
|
|
358
|
+
* Build with `caseWhen()`. Terminate with `.as(alias)` (for use directly in
|
|
359
|
+
* an `attributes: [...]` array) or `.toSQL()` / `.toLiteral()`.
|
|
360
|
+
*/
|
|
361
|
+
class CaseBuilder {
|
|
362
|
+
constructor() {
|
|
363
|
+
this.clauses = [];
|
|
364
|
+
this.hasElse = false;
|
|
365
|
+
}
|
|
366
|
+
when(conditionOrColumn, thenValueOrOperator, value, thenValue) {
|
|
367
|
+
// 4-arg shorthand form: .when(column, operator, value, thenValue)
|
|
368
|
+
if (typeof conditionOrColumn === 'string' && arguments.length >= 4) {
|
|
369
|
+
const opKey = normalizeOperator(thenValueOrOperator);
|
|
370
|
+
const fieldSql = escapeIdentifier(conditionOrColumn);
|
|
371
|
+
const conditionSql = compileFieldOperator(fieldSql, opKey, value);
|
|
372
|
+
this.clauses.push({ conditionSql, resultSql: toSqlFragment(thenValue) });
|
|
373
|
+
return this;
|
|
374
|
+
}
|
|
375
|
+
// 2-arg condition-object form: .when(condition, thenValue)
|
|
376
|
+
const conditionSql = compileCondition(conditionOrColumn);
|
|
377
|
+
this.clauses.push({
|
|
378
|
+
conditionSql,
|
|
379
|
+
resultSql: toSqlFragment(thenValueOrOperator),
|
|
380
|
+
});
|
|
381
|
+
return this;
|
|
382
|
+
}
|
|
383
|
+
/** Set the `ELSE <value>` branch. Optional — omitting it yields an implicit SQL NULL. */
|
|
384
|
+
else(value) {
|
|
385
|
+
this.elseSql = toSqlFragment(value);
|
|
386
|
+
this.hasElse = true;
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
/** Render the `CASE WHEN ... END` SQL fragment (no alias). */
|
|
390
|
+
toSQL() {
|
|
391
|
+
if (this.clauses.length === 0) {
|
|
392
|
+
throw new Error('caseWhen() requires at least one .when(condition, thenValue) branch');
|
|
393
|
+
}
|
|
394
|
+
const whenParts = this.clauses
|
|
395
|
+
.map((c) => `WHEN ${c.conditionSql} THEN ${c.resultSql}`)
|
|
396
|
+
.join(' ');
|
|
397
|
+
const elsePart = this.hasElse ? ` ELSE ${this.elseSql}` : '';
|
|
398
|
+
return `CASE ${whenParts}${elsePart} END`;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Terminal call: returns an `[expr, alias]` tuple compatible with the
|
|
402
|
+
* existing `attributes: [...]` pattern (see `attributeToSql()` in
|
|
403
|
+
* `sql-compiler.ts`, which already knows how to render
|
|
404
|
+
* `[LiteralExpression, alias]` tuples without any core changes).
|
|
405
|
+
*/
|
|
406
|
+
as(alias) {
|
|
407
|
+
return [(0, operators_1.literalExpr)(this.toSQL()), alias];
|
|
408
|
+
}
|
|
409
|
+
/** Use the builder directly wherever a raw SQL literal expression is expected. */
|
|
410
|
+
toLiteral() {
|
|
411
|
+
return (0, operators_1.literalExpr)(this.toSQL());
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
exports.CaseBuilder = CaseBuilder;
|
|
415
|
+
/**
|
|
416
|
+
* Fluent builder for a "simple" CASE expression:
|
|
417
|
+
* CASE expr WHEN val1 THEN result1 WHEN val2 THEN result2 ... ELSE else END
|
|
418
|
+
*
|
|
419
|
+
* Build with `caseOf(expr)`. Terminate with `.as(alias)` or `.toSQL()` /
|
|
420
|
+
* `.toLiteral()`.
|
|
421
|
+
*/
|
|
422
|
+
class SimpleCaseBuilder {
|
|
423
|
+
constructor(expr) {
|
|
424
|
+
this.clauses = [];
|
|
425
|
+
this.hasElse = false;
|
|
426
|
+
// `expr` is typically a column reference (string column name, col(), or
|
|
427
|
+
// any other CaseValue-compatible expression).
|
|
428
|
+
this.exprSql =
|
|
429
|
+
typeof expr === 'string' ? escapeIdentifier(expr) : toSqlFragment(expr);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Add a `WHEN <value> THEN <result>` branch. Repeatable — call multiple
|
|
433
|
+
* times to add multiple branches, evaluated in order.
|
|
434
|
+
*/
|
|
435
|
+
when(value, result) {
|
|
436
|
+
this.clauses.push({ valueSql: toSqlFragment(value), resultSql: toSqlFragment(result) });
|
|
437
|
+
return this;
|
|
438
|
+
}
|
|
439
|
+
/** Set the `ELSE <value>` branch. Optional — omitting it yields an implicit SQL NULL. */
|
|
440
|
+
else(value) {
|
|
441
|
+
this.elseSql = toSqlFragment(value);
|
|
442
|
+
this.hasElse = true;
|
|
443
|
+
return this;
|
|
444
|
+
}
|
|
445
|
+
/** Render the `CASE expr WHEN ... END` SQL fragment (no alias). */
|
|
446
|
+
toSQL() {
|
|
447
|
+
if (this.clauses.length === 0) {
|
|
448
|
+
throw new Error('caseOf() requires at least one .when(value, result) branch');
|
|
449
|
+
}
|
|
450
|
+
const whenParts = this.clauses
|
|
451
|
+
.map((c) => `WHEN ${c.valueSql} THEN ${c.resultSql}`)
|
|
452
|
+
.join(' ');
|
|
453
|
+
const elsePart = this.hasElse ? ` ELSE ${this.elseSql}` : '';
|
|
454
|
+
return `CASE ${this.exprSql} ${whenParts}${elsePart} END`;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Terminal call: returns an `[expr, alias]` tuple compatible with the
|
|
458
|
+
* existing `attributes: [...]` pattern.
|
|
459
|
+
*/
|
|
460
|
+
as(alias) {
|
|
461
|
+
return [(0, operators_1.literalExpr)(this.toSQL()), alias];
|
|
462
|
+
}
|
|
463
|
+
/** Use the builder directly wherever a raw SQL literal expression is expected. */
|
|
464
|
+
toLiteral() {
|
|
465
|
+
return (0, operators_1.literalExpr)(this.toSQL());
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
exports.SimpleCaseBuilder = SimpleCaseBuilder;
|
|
469
|
+
/**
|
|
470
|
+
* Start a "searched" CASE expression:
|
|
471
|
+
* CASE WHEN cond1 THEN result1 [WHEN cond2 THEN result2 ...] [ELSE else] END
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* caseWhen()
|
|
476
|
+
* .when({ age: { [Op.gte]: 18 } }, 'adult')
|
|
477
|
+
* .else('minor')
|
|
478
|
+
* .as('ageGroup')
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
function caseWhen() {
|
|
482
|
+
return new CaseBuilder();
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Start a "simple" CASE expression:
|
|
486
|
+
* CASE expr WHEN val1 THEN result1 [WHEN val2 THEN result2 ...] [ELSE else] END
|
|
487
|
+
*
|
|
488
|
+
* @param expr - The expression being compared. A plain string is treated as
|
|
489
|
+
* a column name (identifier-escaped); pass `col('name')`, `fn(...)`, or
|
|
490
|
+
* `literal(...)` for other expression types.
|
|
491
|
+
*
|
|
492
|
+
* @example
|
|
493
|
+
* ```typescript
|
|
494
|
+
* caseOf('status')
|
|
495
|
+
* .when('active', 1)
|
|
496
|
+
* .when('inactive', 0)
|
|
497
|
+
* .else(null)
|
|
498
|
+
* .as('statusFlag')
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
function caseOf(expr) {
|
|
502
|
+
return new SimpleCaseBuilder(expr);
|
|
503
|
+
}
|