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,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CTE (Common Table Expression) Builder
|
|
4
|
+
* Provides an ORM-like way to build CTEs without raw SQL
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CTEBuilder = void 0;
|
|
8
|
+
exports.createCTEBuilder = createCTEBuilder;
|
|
9
|
+
class CTEBuilder {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.ctes = [];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Add a simple CTE with a base query
|
|
15
|
+
* @example
|
|
16
|
+
* cte.add('active_users', { model: User, baseCase: { where: { active: true } } })
|
|
17
|
+
* cte.add('active_users', { model: 'users', baseCase: { where: { active: true } } })
|
|
18
|
+
*/
|
|
19
|
+
add(name, options) {
|
|
20
|
+
this.ctes.push({
|
|
21
|
+
name,
|
|
22
|
+
model: options?.model,
|
|
23
|
+
columns: options?.columns,
|
|
24
|
+
baseCase: options?.baseCase,
|
|
25
|
+
});
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Add a recursive CTE for hierarchical data
|
|
30
|
+
* @example
|
|
31
|
+
* cte.addRecursive({
|
|
32
|
+
* name: 'org_chart',
|
|
33
|
+
* model: Employee,
|
|
34
|
+
* anchor: { where: { managerId: null } },
|
|
35
|
+
* recursive: { where: { '$org_chart.id$': { [Op.eq]: { [Op.col]: 'Employee.managerId' } } } }
|
|
36
|
+
* })
|
|
37
|
+
*/
|
|
38
|
+
addRecursive(options) {
|
|
39
|
+
this.ctes.push({
|
|
40
|
+
name: options.name,
|
|
41
|
+
model: options.model,
|
|
42
|
+
columns: options.columns,
|
|
43
|
+
baseCase: options.anchor,
|
|
44
|
+
recursiveCase: options.recursive,
|
|
45
|
+
});
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get all CTEs
|
|
50
|
+
*/
|
|
51
|
+
getCTEs() {
|
|
52
|
+
return this.ctes;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get CTE names
|
|
56
|
+
*/
|
|
57
|
+
getCTENames() {
|
|
58
|
+
return this.ctes.map(cte => cte.name);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build the WITH clause SQL
|
|
62
|
+
*/
|
|
63
|
+
toSQL(dialect) {
|
|
64
|
+
return this.ctes.map(cte => cte.name).join(', ');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Clear all CTEs
|
|
68
|
+
*/
|
|
69
|
+
clear() {
|
|
70
|
+
this.ctes = [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.CTEBuilder = CTEBuilder;
|
|
74
|
+
/**
|
|
75
|
+
* Create a new CTE builder
|
|
76
|
+
*/
|
|
77
|
+
function createCTEBuilder() {
|
|
78
|
+
return new CTEBuilder();
|
|
79
|
+
}
|
|
80
|
+
exports.default = { CTEBuilder, createCTEBuilder };
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed aggregate-function builder API.
|
|
4
|
+
*
|
|
5
|
+
* `COUNT`/`SUM`/`AVG`/`MIN`/`MAX` are already reachable through the generic
|
|
6
|
+
* `fn('SUM', col('amount'))` pattern in `src/operators.ts` because those
|
|
7
|
+
* names are ANSI-identical across every dialect this ORM supports. This
|
|
8
|
+
* module fills the real gap: aggregate functions whose *name* (and in some
|
|
9
|
+
* cases clause placement) differs per dialect, most notably string
|
|
10
|
+
* aggregation (`GROUP_CONCAT` / `STRING_AGG` / `LISTAGG` / ClickHouse's
|
|
11
|
+
* array-based approach) and array aggregation (`ARRAY_AGG` / `JSON_ARRAYAGG`
|
|
12
|
+
* / `groupArray`).
|
|
13
|
+
*
|
|
14
|
+
* This module is intentionally self-contained: it does not add any new
|
|
15
|
+
* dispatch branches to the shared SQL compiler (`src/query-builders/sql-compiler.ts`)
|
|
16
|
+
* or touch `src/operators.ts`. Every builder function computes a final,
|
|
17
|
+
* dialect-correct SQL fragment string directly and wraps it with the
|
|
18
|
+
* existing `literalExpr()` helper from `src/operators.ts` so it slots into
|
|
19
|
+
* `attributes: [...]` arrays exactly the way `fn()`/`col()`/`cast()` do
|
|
20
|
+
* today (see `attributeToSql()` in `sql-compiler.ts`, which already knows
|
|
21
|
+
* how to render `{ __type: 'literal', sql }` and `[expr, alias]` tuples).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { count, countDistinct, sum, stringAgg, arrayAgg } from './query-builders/functions/aggregate';
|
|
26
|
+
* import { col } from '../../operators';
|
|
27
|
+
*
|
|
28
|
+
* User.findAll({
|
|
29
|
+
* attributes: [
|
|
30
|
+
* 'department',
|
|
31
|
+
* [count('postgres'), 'total'],
|
|
32
|
+
* [countDistinct('postgres', col('email')), 'uniqueEmails'],
|
|
33
|
+
* [stringAgg('postgres', col('name'), ', ', { orderBy: 'name' }), 'names'],
|
|
34
|
+
* [arrayAgg('postgres', col('id')), 'ids'],
|
|
35
|
+
* ],
|
|
36
|
+
* group: ['department'],
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.count = count;
|
|
42
|
+
exports.countDistinct = countDistinct;
|
|
43
|
+
exports.sum = sum;
|
|
44
|
+
exports.avg = avg;
|
|
45
|
+
exports.min = min;
|
|
46
|
+
exports.max = max;
|
|
47
|
+
exports.stringAgg = stringAgg;
|
|
48
|
+
exports.arrayAgg = arrayAgg;
|
|
49
|
+
const operators_1 = require("../../operators");
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Shared helpers (mirrors the pattern used in ./window.ts)
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
function escapeIdentifier(name) {
|
|
54
|
+
// Support dotted "table.column" references, same convention as col().
|
|
55
|
+
if (name.includes('.')) {
|
|
56
|
+
return name
|
|
57
|
+
.split('.')
|
|
58
|
+
.map((part) => `"${part.replace(/"/g, '""')}"`)
|
|
59
|
+
.join('.');
|
|
60
|
+
}
|
|
61
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Flatten an aggregate-function argument (string, ColExpression,
|
|
65
|
+
* FnExpression, LiteralExpression) into a raw SQL fragment string.
|
|
66
|
+
*/
|
|
67
|
+
function toSqlFragment(arg) {
|
|
68
|
+
if (typeof arg === 'string') {
|
|
69
|
+
return escapeIdentifier(arg);
|
|
70
|
+
}
|
|
71
|
+
if ((0, operators_1.isFnExpression)(arg)) {
|
|
72
|
+
const args = arg.args.map((a) => toSqlFragment(a)).join(', ');
|
|
73
|
+
return `${arg.fn}(${args})`;
|
|
74
|
+
}
|
|
75
|
+
if ((0, operators_1.isColExpression)(arg)) {
|
|
76
|
+
return escapeIdentifier(arg.col);
|
|
77
|
+
}
|
|
78
|
+
if ((0, operators_1.isLiteralExpression)(arg)) {
|
|
79
|
+
// Handles both { __type: 'literal', sql } and { $literal: sql } shapes.
|
|
80
|
+
return arg.sql ?? arg.$literal;
|
|
81
|
+
}
|
|
82
|
+
// Fallback: stringify.
|
|
83
|
+
return String(arg);
|
|
84
|
+
}
|
|
85
|
+
/** Escape a string literal for embedding directly in SQL (single-quote doubling). */
|
|
86
|
+
function escapeStringLiteral(value) {
|
|
87
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
88
|
+
}
|
|
89
|
+
function normalizeOrderBy(orderBy) {
|
|
90
|
+
const entries = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
91
|
+
return entries.map((entry) => typeof entry === 'string' ? { column: entry } : entry);
|
|
92
|
+
}
|
|
93
|
+
/** Render `col ASC, col2 DESC` (no leading "ORDER BY" keyword). */
|
|
94
|
+
function renderOrderByList(orderBy) {
|
|
95
|
+
return normalizeOrderBy(orderBy)
|
|
96
|
+
.map((entry) => {
|
|
97
|
+
const colSql = escapeIdentifier(entry.column);
|
|
98
|
+
return entry.direction ? `${colSql} ${entry.direction}` : colSql;
|
|
99
|
+
})
|
|
100
|
+
.join(', ');
|
|
101
|
+
}
|
|
102
|
+
function unsupported(fnName, dialect, detail) {
|
|
103
|
+
throw new Error(`${fnName}() is not supported on dialect "${dialect}"${detail ? `: ${detail}` : ''}`);
|
|
104
|
+
}
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// count / countDistinct / sum / avg / min / max
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
/**
|
|
109
|
+
* `COUNT(*)` / `COUNT(expr)` — identical ANSI syntax across all 11 dialects.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* count('postgres') // -> COUNT(*)
|
|
114
|
+
* count('postgres', col('id')) // -> COUNT("id")
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
function count(_dialect, expr) {
|
|
118
|
+
const argSql = expr === undefined ? '*' : toSqlFragment(expr);
|
|
119
|
+
return (0, operators_1.literalExpr)(`COUNT(${argSql})`);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* `COUNT(DISTINCT expr)`.
|
|
123
|
+
*
|
|
124
|
+
* Verified across all 11 dialects: MySQL, MariaDB, PostgreSQL, MSSQL,
|
|
125
|
+
* Oracle, SQLite, CockroachDB, Redshift, Db2, and Snowflake all support the
|
|
126
|
+
* exact ANSI `COUNT(DISTINCT expr)` syntax. ClickHouse also parses
|
|
127
|
+
* `COUNT(DISTINCT expr)` (it desugars to an internal uniq-exact
|
|
128
|
+
* implementation), though ClickHouse users may prefer the native
|
|
129
|
+
* `uniqExact(expr)` function for clarity/performance on very large
|
|
130
|
+
* cardinalities — that's a deliberate escape hatch, not a requirement, so
|
|
131
|
+
* `COUNT(DISTINCT expr)` is used here for consistency with the other
|
|
132
|
+
* dialects.
|
|
133
|
+
*/
|
|
134
|
+
function countDistinct(_dialect, expr) {
|
|
135
|
+
return (0, operators_1.literalExpr)(`COUNT(DISTINCT ${toSqlFragment(expr)})`);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* `SUM(expr)` — identical everywhere. Note: SUM of an all-NULL group (or
|
|
139
|
+
* zero input rows) returns SQL NULL (not 0) in every one of these dialects,
|
|
140
|
+
* so callers should COALESCE(...) explicitly if a numeric default is
|
|
141
|
+
* required.
|
|
142
|
+
*/
|
|
143
|
+
function sum(_dialect, expr) {
|
|
144
|
+
return (0, operators_1.literalExpr)(`SUM(${toSqlFragment(expr)})`);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* `AVG(expr)` — identical everywhere. Note: integer-typed columns are
|
|
148
|
+
* averaged as an exact/decimal type in PostgreSQL, MSSQL, Oracle, Db2, and
|
|
149
|
+
* Snowflake, but MySQL/MariaDB/SQLite/ClickHouse promote to floating point;
|
|
150
|
+
* cast explicitly (e.g. `cast(col('x'), 'DECIMAL(10,2)')`) if exact decimal
|
|
151
|
+
* averaging is required across dialects.
|
|
152
|
+
*/
|
|
153
|
+
function avg(_dialect, expr) {
|
|
154
|
+
return (0, operators_1.literalExpr)(`AVG(${toSqlFragment(expr)})`);
|
|
155
|
+
}
|
|
156
|
+
/** `MIN(expr)` — identical everywhere. */
|
|
157
|
+
function min(_dialect, expr) {
|
|
158
|
+
return (0, operators_1.literalExpr)(`MIN(${toSqlFragment(expr)})`);
|
|
159
|
+
}
|
|
160
|
+
/** `MAX(expr)` — identical everywhere. */
|
|
161
|
+
function max(_dialect, expr) {
|
|
162
|
+
return (0, operators_1.literalExpr)(`MAX(${toSqlFragment(expr)})`);
|
|
163
|
+
}
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// stringAgg — the key, most syntax-divergent function in this module
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
/**
|
|
168
|
+
* String aggregation (`GROUP_CONCAT` / `STRING_AGG` / `LISTAGG` / ClickHouse
|
|
169
|
+
* array-concat), dispatched per-dialect. This is the single most
|
|
170
|
+
* error-prone aggregate across dialects, so each branch below documents the
|
|
171
|
+
* exact verified syntax.
|
|
172
|
+
*
|
|
173
|
+
* - **MySQL / MariaDB**:
|
|
174
|
+
* `GROUP_CONCAT([DISTINCT] expr [ORDER BY col [ASC|DESC]] SEPARATOR 'delim')`
|
|
175
|
+
* Clause order is fixed: DISTINCT, then the expression, then ORDER BY,
|
|
176
|
+
* then SEPARATOR — SEPARATOR must come last and is a keyword, not a comma
|
|
177
|
+
* argument.
|
|
178
|
+
*
|
|
179
|
+
* - **SQLite**: `GROUP_CONCAT(expr, 'delim')` (2-argument form; the
|
|
180
|
+
* separator defaults to `,` if omitted). SQLite's GROUP_CONCAT does
|
|
181
|
+
* **not** support an `ORDER BY` clause inside the function call at all —
|
|
182
|
+
* if `options.orderBy` is passed for SQLite this throws, since silently
|
|
183
|
+
* ignoring it would produce non-deterministic ordering that looks
|
|
184
|
+
* intentional. Pre-sort via a subquery/CTE if ordering is required.
|
|
185
|
+
* SQLite's DISTINCT variant (`GROUP_CONCAT(DISTINCT expr)`) is supported
|
|
186
|
+
* but forces the default comma separator (a custom separator combined
|
|
187
|
+
* with DISTINCT is not supported by SQLite) — this also throws if both
|
|
188
|
+
* `distinct` and a non-default delimiter are requested together.
|
|
189
|
+
*
|
|
190
|
+
* - **PostgreSQL / CockroachDB**: `STRING_AGG([DISTINCT] expr, 'delim' [ORDER BY col [ASC|DESC]])`
|
|
191
|
+
* — the ORDER BY clause sits *inside* the same parentheses as the
|
|
192
|
+
* delimiter argument (no WITHIN GROUP).
|
|
193
|
+
*
|
|
194
|
+
* - **MSSQL**: `STRING_AGG(expr, 'delim') WITHIN GROUP (ORDER BY col [ASC|DESC])`
|
|
195
|
+
* — note this is a genuinely different clause *placement* than Postgres:
|
|
196
|
+
* the ORDER BY is a separate `WITHIN GROUP (...)` clause appended after
|
|
197
|
+
* the function call, not inline inside the argument list. MSSQL's
|
|
198
|
+
* `STRING_AGG` also does **not** support a `DISTINCT` argument at all
|
|
199
|
+
* (unlike Postgres) — this throws if `distinct: true` is requested.
|
|
200
|
+
*
|
|
201
|
+
* - **Oracle / Db2**: `LISTAGG([DISTINCT] expr, 'delim') WITHIN GROUP (ORDER BY col [ASC|DESC])`
|
|
202
|
+
* — same WITHIN GROUP placement as MSSQL. Oracle supports `DISTINCT`
|
|
203
|
+
* inside LISTAGG since 19c; Db2 LUW's LISTAGG also supports `DISTINCT`.
|
|
204
|
+
*
|
|
205
|
+
* - **Redshift**: `LISTAGG([DISTINCT] expr, 'delim') WITHIN GROUP (ORDER BY col [ASC|DESC])`
|
|
206
|
+
* — Redshift's aggregate string function has always been `LISTAGG`
|
|
207
|
+
* (matching Oracle/Db2 syntax), not `STRING_AGG`. AWS did add a
|
|
208
|
+
* Postgres-compatible `STRING_AGG` alias to Redshift more recently, but
|
|
209
|
+
* `LISTAGG` is the long-standing, universally-available, and
|
|
210
|
+
* ORDER-BY-capable form, so it's used here for correctness.
|
|
211
|
+
*
|
|
212
|
+
* - **Snowflake**: `LISTAGG([DISTINCT] expr, 'delim') WITHIN GROUP (ORDER BY col [ASC|DESC])`
|
|
213
|
+
* — Snowflake's string aggregation function is `LISTAGG` (Oracle-style),
|
|
214
|
+
* *not* `STRING_AGG` — Snowflake has no `STRING_AGG` function.
|
|
215
|
+
*
|
|
216
|
+
* - **ClickHouse**: no native `GROUP_CONCAT`/`STRING_AGG`/`LISTAGG`. The
|
|
217
|
+
* verified, version-portable idiom is
|
|
218
|
+
* `arrayStringConcat(groupArray(expr), 'delim')` (distinct:
|
|
219
|
+
* `arrayStringConcat(groupUniqArray(expr), 'delim')`). Newer ClickHouse
|
|
220
|
+
* releases (24.x+) also ship a MySQL-compatible `groupConcat(expr)`
|
|
221
|
+
* function, but `arrayStringConcat(groupArray(...), ...)` works on every
|
|
222
|
+
* supported version so it's used as the default here. ClickHouse's
|
|
223
|
+
* `groupArray` has no inline `ORDER BY` sub-clause (it preserves block
|
|
224
|
+
* processing order, not a guaranteed sort) — `options.orderBy` therefore
|
|
225
|
+
* throws for ClickHouse; pre-sort via a subquery if ordering matters.
|
|
226
|
+
*/
|
|
227
|
+
function stringAgg(dialect, expr, delimiter, options = {}) {
|
|
228
|
+
const { orderBy, distinct } = options;
|
|
229
|
+
const exprSql = toSqlFragment(expr);
|
|
230
|
+
const delimSql = escapeStringLiteral(delimiter);
|
|
231
|
+
switch (dialect) {
|
|
232
|
+
case 'mysql':
|
|
233
|
+
case 'mariadb': {
|
|
234
|
+
const parts = [];
|
|
235
|
+
if (distinct)
|
|
236
|
+
parts.push('DISTINCT');
|
|
237
|
+
parts.push(exprSql);
|
|
238
|
+
let sql = `GROUP_CONCAT(${parts.join(' ')}`;
|
|
239
|
+
if (orderBy) {
|
|
240
|
+
sql += ` ORDER BY ${renderOrderByList(orderBy)}`;
|
|
241
|
+
}
|
|
242
|
+
sql += ` SEPARATOR ${delimSql})`;
|
|
243
|
+
return (0, operators_1.literalExpr)(sql);
|
|
244
|
+
}
|
|
245
|
+
case 'sqlite': {
|
|
246
|
+
if (orderBy) {
|
|
247
|
+
unsupported('stringAgg', dialect, "SQLite's GROUP_CONCAT does not support an ORDER BY clause; pre-sort via a subquery instead");
|
|
248
|
+
}
|
|
249
|
+
if (distinct) {
|
|
250
|
+
if (delimiter !== ',') {
|
|
251
|
+
unsupported('stringAgg', dialect, 'SQLite GROUP_CONCAT(DISTINCT ...) only supports the default comma separator, not a custom delimiter');
|
|
252
|
+
}
|
|
253
|
+
return (0, operators_1.literalExpr)(`GROUP_CONCAT(DISTINCT ${exprSql})`);
|
|
254
|
+
}
|
|
255
|
+
return (0, operators_1.literalExpr)(`GROUP_CONCAT(${exprSql}, ${delimSql})`);
|
|
256
|
+
}
|
|
257
|
+
case 'postgres':
|
|
258
|
+
case 'cockroachdb': {
|
|
259
|
+
const argSql = distinct ? `DISTINCT ${exprSql}` : exprSql;
|
|
260
|
+
let sql = `STRING_AGG(${argSql}, ${delimSql}`;
|
|
261
|
+
if (orderBy) {
|
|
262
|
+
sql += ` ORDER BY ${renderOrderByList(orderBy)}`;
|
|
263
|
+
}
|
|
264
|
+
sql += ')';
|
|
265
|
+
return (0, operators_1.literalExpr)(sql);
|
|
266
|
+
}
|
|
267
|
+
case 'mssql': {
|
|
268
|
+
if (distinct) {
|
|
269
|
+
unsupported('stringAgg', dialect, 'MSSQL STRING_AGG does not support DISTINCT');
|
|
270
|
+
}
|
|
271
|
+
let sql = `STRING_AGG(${exprSql}, ${delimSql})`;
|
|
272
|
+
if (orderBy) {
|
|
273
|
+
sql += ` WITHIN GROUP (ORDER BY ${renderOrderByList(orderBy)})`;
|
|
274
|
+
}
|
|
275
|
+
return (0, operators_1.literalExpr)(sql);
|
|
276
|
+
}
|
|
277
|
+
case 'oracle':
|
|
278
|
+
case 'db2':
|
|
279
|
+
case 'redshift':
|
|
280
|
+
case 'snowflake': {
|
|
281
|
+
const argSql = distinct ? `DISTINCT ${exprSql}` : exprSql;
|
|
282
|
+
let sql = `LISTAGG(${argSql}, ${delimSql})`;
|
|
283
|
+
if (orderBy) {
|
|
284
|
+
sql += ` WITHIN GROUP (ORDER BY ${renderOrderByList(orderBy)})`;
|
|
285
|
+
}
|
|
286
|
+
return (0, operators_1.literalExpr)(sql);
|
|
287
|
+
}
|
|
288
|
+
case 'clickhouse': {
|
|
289
|
+
if (orderBy) {
|
|
290
|
+
unsupported('stringAgg', dialect, "ClickHouse's groupArray()/arrayStringConcat() has no inline ORDER BY; pre-sort via a subquery instead");
|
|
291
|
+
}
|
|
292
|
+
const arrayFn = distinct ? 'groupUniqArray' : 'groupArray';
|
|
293
|
+
return (0, operators_1.literalExpr)(`arrayStringConcat(${arrayFn}(${exprSql}), ${delimSql})`);
|
|
294
|
+
}
|
|
295
|
+
default: {
|
|
296
|
+
// Exhaustiveness guard for future Dialect additions.
|
|
297
|
+
throw new Error(`stringAgg(): unhandled dialect "${dialect}"`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// arrayAgg
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
/**
|
|
305
|
+
* Aggregate values into an array/JSON-array, dispatched per-dialect.
|
|
306
|
+
*
|
|
307
|
+
* - **PostgreSQL / CockroachDB**: native `ARRAY_AGG([DISTINCT] expr [ORDER BY col [ASC|DESC]])`.
|
|
308
|
+
* - **Snowflake**: native `ARRAY_AGG([DISTINCT] expr) [WITHIN GROUP (ORDER BY col [ASC|DESC])]`
|
|
309
|
+
* — Snowflake's ARRAY_AGG uses the same WITHIN GROUP placement as LISTAGG,
|
|
310
|
+
* not Postgres's inline ORDER BY.
|
|
311
|
+
* - **ClickHouse**: native `groupArray(expr)` (distinct: `groupUniqArray(expr)`);
|
|
312
|
+
* returns ClickHouse's native Array(T) type. No inline ORDER BY support
|
|
313
|
+
* (same limitation as `stringAgg`) — throws if `orderBy` is requested.
|
|
314
|
+
* - **MySQL (5.7.22+) / MariaDB (10.5+)**: no `ARRAY_AGG`, but both support
|
|
315
|
+
* the real `JSON_ARRAYAGG(expr)` function, used here as a JSON-array
|
|
316
|
+
* fallback. Neither dialect's `JSON_ARRAYAGG` supports `DISTINCT` or
|
|
317
|
+
* `ORDER BY` — both throw if requested.
|
|
318
|
+
* - **Oracle (12.2+) / Db2 (11.5+)**: no `ARRAY_AGG`, but both support the
|
|
319
|
+
* real SQL:2016 `JSON_ARRAYAGG(expr)` function, used as a JSON-array
|
|
320
|
+
* fallback here too. `DISTINCT`/`ORDER BY` throw (not supported by either
|
|
321
|
+
* engine's JSON_ARRAYAGG).
|
|
322
|
+
* - **SQLite**: no `ARRAY_AGG`, but has the real native `json_group_array(expr)`
|
|
323
|
+
* aggregate, used as the fallback. `DISTINCT`/`ORDER BY` throw (not
|
|
324
|
+
* supported by SQLite's json_group_array).
|
|
325
|
+
* - **MSSQL / Redshift**: no real array-aggregation concept and no reliable
|
|
326
|
+
* scalar-aggregate JSON-array equivalent (MSSQL's JSON array construction
|
|
327
|
+
* is via `FOR JSON PATH` on a full query shape, not an aggregate function
|
|
328
|
+
* usable inline in a SELECT list; Redshift has no JSON_ARRAYAGG). Both
|
|
329
|
+
* throw a clear "not supported" error rather than faking an unreliable
|
|
330
|
+
* workaround.
|
|
331
|
+
*/
|
|
332
|
+
function arrayAgg(dialect, expr, options = {}) {
|
|
333
|
+
const { orderBy, distinct } = options;
|
|
334
|
+
const exprSql = toSqlFragment(expr);
|
|
335
|
+
switch (dialect) {
|
|
336
|
+
case 'postgres':
|
|
337
|
+
case 'cockroachdb': {
|
|
338
|
+
const argSql = distinct ? `DISTINCT ${exprSql}` : exprSql;
|
|
339
|
+
let sql = `ARRAY_AGG(${argSql}`;
|
|
340
|
+
if (orderBy) {
|
|
341
|
+
sql += ` ORDER BY ${renderOrderByList(orderBy)}`;
|
|
342
|
+
}
|
|
343
|
+
sql += ')';
|
|
344
|
+
return (0, operators_1.literalExpr)(sql);
|
|
345
|
+
}
|
|
346
|
+
case 'snowflake': {
|
|
347
|
+
const argSql = distinct ? `DISTINCT ${exprSql}` : exprSql;
|
|
348
|
+
let sql = `ARRAY_AGG(${argSql})`;
|
|
349
|
+
if (orderBy) {
|
|
350
|
+
sql += ` WITHIN GROUP (ORDER BY ${renderOrderByList(orderBy)})`;
|
|
351
|
+
}
|
|
352
|
+
return (0, operators_1.literalExpr)(sql);
|
|
353
|
+
}
|
|
354
|
+
case 'clickhouse': {
|
|
355
|
+
if (orderBy) {
|
|
356
|
+
unsupported('arrayAgg', dialect, "ClickHouse's groupArray() has no inline ORDER BY; pre-sort via a subquery instead");
|
|
357
|
+
}
|
|
358
|
+
const arrayFn = distinct ? 'groupUniqArray' : 'groupArray';
|
|
359
|
+
return (0, operators_1.literalExpr)(`${arrayFn}(${exprSql})`);
|
|
360
|
+
}
|
|
361
|
+
case 'mysql':
|
|
362
|
+
case 'mariadb': {
|
|
363
|
+
if (distinct || orderBy) {
|
|
364
|
+
unsupported('arrayAgg', dialect, `${dialect === 'mysql' ? 'MySQL' : 'MariaDB'}'s JSON_ARRAYAGG (used as the ARRAY_AGG fallback) does not support DISTINCT or ORDER BY`);
|
|
365
|
+
}
|
|
366
|
+
return (0, operators_1.literalExpr)(`JSON_ARRAYAGG(${exprSql})`);
|
|
367
|
+
}
|
|
368
|
+
case 'oracle':
|
|
369
|
+
case 'db2': {
|
|
370
|
+
if (distinct || orderBy) {
|
|
371
|
+
unsupported('arrayAgg', dialect, `${dialect === 'oracle' ? 'Oracle' : 'Db2'}'s JSON_ARRAYAGG (used as the ARRAY_AGG fallback) does not support DISTINCT or ORDER BY`);
|
|
372
|
+
}
|
|
373
|
+
return (0, operators_1.literalExpr)(`JSON_ARRAYAGG(${exprSql})`);
|
|
374
|
+
}
|
|
375
|
+
case 'sqlite': {
|
|
376
|
+
if (distinct || orderBy) {
|
|
377
|
+
unsupported('arrayAgg', dialect, "SQLite's json_group_array (used as the ARRAY_AGG fallback) does not support DISTINCT or ORDER BY");
|
|
378
|
+
}
|
|
379
|
+
return (0, operators_1.literalExpr)(`json_group_array(${exprSql})`);
|
|
380
|
+
}
|
|
381
|
+
case 'mssql':
|
|
382
|
+
unsupported('arrayAgg', dialect, 'MSSQL has no ARRAY_AGG or scalar JSON-array aggregate function usable in a SELECT list (FOR JSON PATH operates on whole query shapes, not individual aggregate expressions)');
|
|
383
|
+
case 'redshift':
|
|
384
|
+
unsupported('arrayAgg', dialect, 'Redshift has no ARRAY_AGG and no JSON_ARRAYAGG equivalent');
|
|
385
|
+
default: {
|
|
386
|
+
// Exhaustiveness guard for future Dialect additions.
|
|
387
|
+
throw new Error(`arrayAgg(): unhandled dialect "${dialect}"`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|