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,695 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed date/time function builder API.
|
|
4
|
+
*
|
|
5
|
+
* Date truncation, date arithmetic (add/subtract), date-difference, "current
|
|
6
|
+
* date/time" helpers, and best-effort date formatting differ WILDLY across
|
|
7
|
+
* the 11 SQL dialects this ORM supports (Postgres `DATE_TRUNC`, MySQL
|
|
8
|
+
* `DATE_ADD`/`DATEDIFF`, MSSQL `DATEADD`/`DATEDIFF`, Oracle `TRUNC`/
|
|
9
|
+
* `ADD_MONTHS`, SQLite `strftime`/`julianday`, etc). This module gives callers
|
|
10
|
+
* a single typed entry point per capability so they never have to hand-write
|
|
11
|
+
* dialect-specific SQL strings themselves.
|
|
12
|
+
*
|
|
13
|
+
* This module is intentionally self-contained, mirroring the approach taken
|
|
14
|
+
* in `src/query-builders/functions/window.ts`: it does NOT add any new
|
|
15
|
+
* dispatch branches to the shared SQL compiler (`src/query-builders/
|
|
16
|
+
* sql-compiler.ts`) or to `src/operators.ts`. Every function below computes
|
|
17
|
+
* a final, dialect-correct SQL fragment string directly and wraps it with the
|
|
18
|
+
* existing `literal()` helper from `src/operators.ts` so it slots into
|
|
19
|
+
* `attributes: [...]` / `where` clauses exactly the way `fn()`/`col()`/
|
|
20
|
+
* `cast()`/`extract()` do today.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { dateTrunc, dateAdd, dateDiff, now, formatDate } from './query-builders/functions/datetime';
|
|
25
|
+
* import { col } from '../../operators';
|
|
26
|
+
*
|
|
27
|
+
* User.findAll({
|
|
28
|
+
* attributes: [
|
|
29
|
+
* 'id',
|
|
30
|
+
* dateTrunc('postgres', 'month', col('createdAt')).as('signupMonth'),
|
|
31
|
+
* dateAdd('postgres', col('createdAt'), 30, 'day').as('trialEnds'),
|
|
32
|
+
* dateDiff('mysql', 'day', col('startDate'), col('endDate')).as('durationDays'),
|
|
33
|
+
* now('sqlite').as('rightNow'),
|
|
34
|
+
* formatDate('postgres', col('createdAt'), 'YYYY-MM-DD').as('createdAtStr'),
|
|
35
|
+
* ],
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.dateTrunc = dateTrunc;
|
|
41
|
+
exports.dateAdd = dateAdd;
|
|
42
|
+
exports.dateSub = dateSub;
|
|
43
|
+
exports.dateDiff = dateDiff;
|
|
44
|
+
exports.now = now;
|
|
45
|
+
exports.currentDate = currentDate;
|
|
46
|
+
exports.currentTimestamp = currentTimestamp;
|
|
47
|
+
exports.formatDate = formatDate;
|
|
48
|
+
const operators_1 = require("../../operators");
|
|
49
|
+
function makeExpression(sql) {
|
|
50
|
+
const lit = (0, operators_1.literal)(sql);
|
|
51
|
+
return {
|
|
52
|
+
__type: 'literal',
|
|
53
|
+
sql,
|
|
54
|
+
$literal: lit.$literal,
|
|
55
|
+
as(alias) {
|
|
56
|
+
return [this, alias];
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function isDateTimeExpression(value) {
|
|
61
|
+
return value && value.__type === 'literal' && typeof value.sql === 'string' && typeof value.as === 'function';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Escape a plain identifier (or `table.column` dotted path) using ANSI
|
|
65
|
+
* double-quote convention. This mirrors `escapeIdentifier()` in
|
|
66
|
+
* `window.ts` for consistency across the `functions/` family; the shared
|
|
67
|
+
* compiler normalizes/re-quotes identifiers per dialect downstream the same
|
|
68
|
+
* way it already does for `col()`/`fn()` output.
|
|
69
|
+
*/
|
|
70
|
+
function escapeIdentifier(name) {
|
|
71
|
+
if (name.includes('.')) {
|
|
72
|
+
return name
|
|
73
|
+
.split('.')
|
|
74
|
+
.map((part) => `"${part.replace(/"/g, '""')}"`)
|
|
75
|
+
.join('.');
|
|
76
|
+
}
|
|
77
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
78
|
+
}
|
|
79
|
+
/** Format a JS Date as a portable `'YYYY-MM-DD HH:MM:SS'` SQL string literal. */
|
|
80
|
+
function formatDateLiteral(date) {
|
|
81
|
+
const iso = date.toISOString(); // e.g. 2024-01-02T03:04:05.000Z
|
|
82
|
+
const value = iso.replace('T', ' ').replace('Z', '').replace(/\.\d+$/, '');
|
|
83
|
+
return `'${value}'`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Flatten a date/time argument (string column name, ColExpression,
|
|
87
|
+
* FnExpression, LiteralExpression, DateTimeExpression, Date, number) into a
|
|
88
|
+
* raw SQL fragment string. Plain strings are treated as column names (same
|
|
89
|
+
* convention as `extract()`/`fn()`/`col()` in `src/operators.ts`).
|
|
90
|
+
*/
|
|
91
|
+
function toSqlFragment(arg) {
|
|
92
|
+
if (arg === null || arg === undefined)
|
|
93
|
+
return 'NULL';
|
|
94
|
+
if (typeof arg === 'number')
|
|
95
|
+
return String(arg);
|
|
96
|
+
if (arg instanceof Date)
|
|
97
|
+
return formatDateLiteral(arg);
|
|
98
|
+
if (typeof arg === 'string')
|
|
99
|
+
return escapeIdentifier(arg);
|
|
100
|
+
if (isDateTimeExpression(arg))
|
|
101
|
+
return arg.sql;
|
|
102
|
+
if ((0, operators_1.isFnExpression)(arg)) {
|
|
103
|
+
const args = arg.args.map((a) => toSqlFragment(a)).join(', ');
|
|
104
|
+
return `${arg.fn}(${args})`;
|
|
105
|
+
}
|
|
106
|
+
if ((0, operators_1.isColExpression)(arg))
|
|
107
|
+
return escapeIdentifier(arg.col);
|
|
108
|
+
if ((0, operators_1.isLiteralExpression)(arg)) {
|
|
109
|
+
return '$literal' in arg ? arg.$literal : arg.sql;
|
|
110
|
+
}
|
|
111
|
+
// Fallback: stringify
|
|
112
|
+
return String(arg);
|
|
113
|
+
}
|
|
114
|
+
function isPostgresLikeTrunc(dialect) {
|
|
115
|
+
// Postgres, CockroachDB, and Redshift all support the real DATE_TRUNC('unit', expr)
|
|
116
|
+
// function with identical syntax/unit-name semantics (Redshift and CockroachDB are
|
|
117
|
+
// both Postgres-wire/SQL-derived and inherit this function verbatim).
|
|
118
|
+
return dialect === 'postgres' || dialect === 'cockroachdb' || dialect === 'redshift';
|
|
119
|
+
}
|
|
120
|
+
// ============================================================================
|
|
121
|
+
// dateTrunc
|
|
122
|
+
// ============================================================================
|
|
123
|
+
/**
|
|
124
|
+
* Truncate a date/timestamp expression down to the start of the given unit
|
|
125
|
+
* boundary (year/quarter/month/week/day/hour/minute/second).
|
|
126
|
+
*
|
|
127
|
+
* Dialect notes:
|
|
128
|
+
* - postgres / cockroachdb / redshift: native `DATE_TRUNC('unit', expr)`.
|
|
129
|
+
* - mysql / mariadb: NO native DATE_TRUNC exists (verified) — built via
|
|
130
|
+
* `DATE_FORMAT`/string-concat + `STR_TO_DATE` workarounds.
|
|
131
|
+
* - mssql: uses `DATETRUNC(datepart, expr)`, added in SQL Server 2022. For
|
|
132
|
+
* older MSSQL versions there is no native equivalent; this targets modern
|
|
133
|
+
* SQL Server.
|
|
134
|
+
* - oracle: `TRUNC(expr, fmt)`. Oracle's TRUNC only truncates down to
|
|
135
|
+
* hour/minute ('HH24'/'MI'); truncating to 'second' is a no-op since DATE
|
|
136
|
+
* values already carry second precision, so `expr` is returned unchanged.
|
|
137
|
+
* - sqlite: no native function — built via `strftime()`/string-concat.
|
|
138
|
+
* - db2: no native DATE_TRUNC — built via `VARCHAR_FORMAT`/`TIMESTAMP_FORMAT`
|
|
139
|
+
* round-tripping (Db2's TO_CHAR/TO_DATE equivalents) plus `DAYOFWEEK()`
|
|
140
|
+
* for week truncation.
|
|
141
|
+
* - snowflake: native `DATE_TRUNC('unit', expr)`.
|
|
142
|
+
* - clickhouse: dedicated `toStartOfYear`/`toStartOfQuarter`/`toStartOfMonth`/
|
|
143
|
+
* `toStartOfWeek`/`toStartOfDay`/`toStartOfHour`/`toStartOfMinute`/
|
|
144
|
+
* `toStartOfSecond` functions (genuinely different function-per-unit API,
|
|
145
|
+
* not a single parameterized function).
|
|
146
|
+
*/
|
|
147
|
+
function dateTrunc(dialect, unit, expr) {
|
|
148
|
+
const e = toSqlFragment(expr);
|
|
149
|
+
if (isPostgresLikeTrunc(dialect) || dialect === 'snowflake') {
|
|
150
|
+
return makeExpression(`DATE_TRUNC('${unit}', ${e})`);
|
|
151
|
+
}
|
|
152
|
+
switch (dialect) {
|
|
153
|
+
case 'mysql':
|
|
154
|
+
case 'mariadb': {
|
|
155
|
+
switch (unit) {
|
|
156
|
+
case 'year':
|
|
157
|
+
return makeExpression(`DATE_FORMAT(${e}, '%Y-01-01 00:00:00')`);
|
|
158
|
+
case 'quarter':
|
|
159
|
+
return makeExpression(`STR_TO_DATE(CONCAT(DATE_FORMAT(${e}, '%Y-'), LPAD((QUARTER(${e}) - 1) * 3 + 1, 2, '0'), '-01 00:00:00'), '%Y-%m-%d %H:%i:%s')`);
|
|
160
|
+
case 'month':
|
|
161
|
+
return makeExpression(`DATE_FORMAT(${e}, '%Y-%m-01 00:00:00')`);
|
|
162
|
+
case 'week':
|
|
163
|
+
return makeExpression(`DATE_SUB(DATE(${e}), INTERVAL WEEKDAY(${e}) DAY)`);
|
|
164
|
+
case 'day':
|
|
165
|
+
return makeExpression(`DATE_FORMAT(${e}, '%Y-%m-%d 00:00:00')`);
|
|
166
|
+
case 'hour':
|
|
167
|
+
return makeExpression(`DATE_FORMAT(${e}, '%Y-%m-%d %H:00:00')`);
|
|
168
|
+
case 'minute':
|
|
169
|
+
return makeExpression(`DATE_FORMAT(${e}, '%Y-%m-%d %H:%i:00')`);
|
|
170
|
+
case 'second':
|
|
171
|
+
return makeExpression(`DATE_FORMAT(${e}, '%Y-%m-%d %H:%i:%s')`);
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
case 'mssql': {
|
|
176
|
+
// SQL Server 2022+ DATETRUNC(datepart, date) accepts these unit names directly.
|
|
177
|
+
return makeExpression(`DATETRUNC(${unit}, ${e})`);
|
|
178
|
+
}
|
|
179
|
+
case 'oracle': {
|
|
180
|
+
switch (unit) {
|
|
181
|
+
case 'year':
|
|
182
|
+
return makeExpression(`TRUNC(${e}, 'YYYY')`);
|
|
183
|
+
case 'quarter':
|
|
184
|
+
return makeExpression(`TRUNC(${e}, 'Q')`);
|
|
185
|
+
case 'month':
|
|
186
|
+
return makeExpression(`TRUNC(${e}, 'MM')`);
|
|
187
|
+
case 'week':
|
|
188
|
+
return makeExpression(`TRUNC(${e}, 'IW')`);
|
|
189
|
+
case 'day':
|
|
190
|
+
return makeExpression(`TRUNC(${e}, 'DD')`);
|
|
191
|
+
case 'hour':
|
|
192
|
+
return makeExpression(`TRUNC(${e}, 'HH24')`);
|
|
193
|
+
case 'minute':
|
|
194
|
+
return makeExpression(`TRUNC(${e}, 'MI')`);
|
|
195
|
+
case 'second':
|
|
196
|
+
// Oracle DATE has only second precision already; no smaller truncation exists.
|
|
197
|
+
return makeExpression(`${e}`);
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
case 'sqlite': {
|
|
202
|
+
switch (unit) {
|
|
203
|
+
case 'year':
|
|
204
|
+
return makeExpression(`strftime('%Y-01-01 00:00:00', ${e})`);
|
|
205
|
+
case 'quarter':
|
|
206
|
+
return makeExpression(`(strftime('%Y-', ${e}) || printf('%02d', ((CAST(strftime('%m', ${e}) AS INTEGER) - 1) / 3) * 3 + 1) || '-01 00:00:00')`);
|
|
207
|
+
case 'month':
|
|
208
|
+
return makeExpression(`strftime('%Y-%m-01 00:00:00', ${e})`);
|
|
209
|
+
case 'week':
|
|
210
|
+
return makeExpression(`date(${e}, '-' || strftime('%w', ${e}) || ' days')`);
|
|
211
|
+
case 'day':
|
|
212
|
+
return makeExpression(`strftime('%Y-%m-%d 00:00:00', ${e})`);
|
|
213
|
+
case 'hour':
|
|
214
|
+
return makeExpression(`strftime('%Y-%m-%d %H:00:00', ${e})`);
|
|
215
|
+
case 'minute':
|
|
216
|
+
return makeExpression(`strftime('%Y-%m-%d %H:%M:00', ${e})`);
|
|
217
|
+
case 'second':
|
|
218
|
+
return makeExpression(`strftime('%Y-%m-%d %H:%M:%S', ${e})`);
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
case 'db2': {
|
|
223
|
+
switch (unit) {
|
|
224
|
+
case 'year':
|
|
225
|
+
return makeExpression(`TIMESTAMP_FORMAT(VARCHAR_FORMAT(${e}, 'YYYY') || '-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')`);
|
|
226
|
+
case 'quarter':
|
|
227
|
+
return makeExpression(`TIMESTAMP_FORMAT(VARCHAR_FORMAT(${e}, 'YYYY') || '-' || LPAD((QUARTER(${e}) - 1) * 3 + 1, 2, '0') || '-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')`);
|
|
228
|
+
case 'month':
|
|
229
|
+
return makeExpression(`TIMESTAMP_FORMAT(VARCHAR_FORMAT(${e}, 'YYYY-MM') || '-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')`);
|
|
230
|
+
case 'week':
|
|
231
|
+
return makeExpression(`(CAST(${e} AS DATE) - (DAYOFWEEK(${e}) - 1) DAYS)`);
|
|
232
|
+
case 'day':
|
|
233
|
+
return makeExpression(`TIMESTAMP_FORMAT(VARCHAR_FORMAT(${e}, 'YYYY-MM-DD') || ' 00:00:00', 'YYYY-MM-DD HH24:MI:SS')`);
|
|
234
|
+
case 'hour':
|
|
235
|
+
return makeExpression(`TIMESTAMP_FORMAT(VARCHAR_FORMAT(${e}, 'YYYY-MM-DD HH24') || ':00:00', 'YYYY-MM-DD HH24:MI:SS')`);
|
|
236
|
+
case 'minute':
|
|
237
|
+
return makeExpression(`TIMESTAMP_FORMAT(VARCHAR_FORMAT(${e}, 'YYYY-MM-DD HH24:MI') || ':00', 'YYYY-MM-DD HH24:MI:SS')`);
|
|
238
|
+
case 'second':
|
|
239
|
+
return makeExpression(`TIMESTAMP_FORMAT(VARCHAR_FORMAT(${e}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS')`);
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case 'clickhouse': {
|
|
244
|
+
switch (unit) {
|
|
245
|
+
case 'year':
|
|
246
|
+
return makeExpression(`toStartOfYear(${e})`);
|
|
247
|
+
case 'quarter':
|
|
248
|
+
return makeExpression(`toStartOfQuarter(${e})`);
|
|
249
|
+
case 'month':
|
|
250
|
+
return makeExpression(`toStartOfMonth(${e})`);
|
|
251
|
+
case 'week':
|
|
252
|
+
return makeExpression(`toStartOfWeek(${e})`);
|
|
253
|
+
case 'day':
|
|
254
|
+
return makeExpression(`toStartOfDay(${e})`);
|
|
255
|
+
case 'hour':
|
|
256
|
+
return makeExpression(`toStartOfHour(${e})`);
|
|
257
|
+
case 'minute':
|
|
258
|
+
return makeExpression(`toStartOfMinute(${e})`);
|
|
259
|
+
case 'second':
|
|
260
|
+
return makeExpression(`toStartOfSecond(${e})`);
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Should be unreachable for supported dialects, but keep a safe fallback.
|
|
266
|
+
return makeExpression(`DATE_TRUNC('${unit}', ${e})`);
|
|
267
|
+
}
|
|
268
|
+
// ============================================================================
|
|
269
|
+
// dateAdd / dateSub
|
|
270
|
+
// ============================================================================
|
|
271
|
+
function dateAddInternal(dialect, expr, signedAmount, unit) {
|
|
272
|
+
const e = toSqlFragment(expr);
|
|
273
|
+
const amount = signedAmount;
|
|
274
|
+
switch (dialect) {
|
|
275
|
+
case 'postgres':
|
|
276
|
+
case 'cockroachdb': {
|
|
277
|
+
// Native interval arithmetic.
|
|
278
|
+
const sign = amount < 0 ? '-' : '+';
|
|
279
|
+
return makeExpression(`${e} ${sign} INTERVAL '${Math.abs(amount)} ${unit}'`);
|
|
280
|
+
}
|
|
281
|
+
case 'redshift': {
|
|
282
|
+
// Redshift has a genuine DATEADD(datepart, number, date) function (SQL-Server-like),
|
|
283
|
+
// distinct from Postgres's interval-literal arithmetic even though Redshift is
|
|
284
|
+
// otherwise Postgres-derived.
|
|
285
|
+
return makeExpression(`DATEADD(${unit}, ${amount}, ${e})`);
|
|
286
|
+
}
|
|
287
|
+
case 'mysql':
|
|
288
|
+
case 'mariadb': {
|
|
289
|
+
const fn = amount < 0 ? 'DATE_SUB' : 'DATE_ADD';
|
|
290
|
+
return makeExpression(`${fn}(${e}, INTERVAL ${Math.abs(amount)} ${unit.toUpperCase()})`);
|
|
291
|
+
}
|
|
292
|
+
case 'mssql': {
|
|
293
|
+
return makeExpression(`DATEADD(${unit}, ${amount}, ${e})`);
|
|
294
|
+
}
|
|
295
|
+
case 'oracle': {
|
|
296
|
+
switch (unit) {
|
|
297
|
+
case 'year':
|
|
298
|
+
return makeExpression(`ADD_MONTHS(${e}, ${amount * 12})`);
|
|
299
|
+
case 'month':
|
|
300
|
+
return makeExpression(`ADD_MONTHS(${e}, ${amount})`);
|
|
301
|
+
case 'day':
|
|
302
|
+
return makeExpression(`(${e} + ${amount})`);
|
|
303
|
+
case 'hour':
|
|
304
|
+
return makeExpression(`(${e} + ${amount} / 24)`);
|
|
305
|
+
case 'minute':
|
|
306
|
+
return makeExpression(`(${e} + ${amount} / 1440)`);
|
|
307
|
+
case 'second':
|
|
308
|
+
return makeExpression(`(${e} + ${amount} / 86400)`);
|
|
309
|
+
}
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case 'sqlite': {
|
|
313
|
+
const plural = `${unit}${Math.abs(amount) === 1 ? '' : 's'}`;
|
|
314
|
+
const sign = amount < 0 ? '-' : '+';
|
|
315
|
+
return makeExpression(`datetime(${e}, '${sign}${Math.abs(amount)} ${plural}')`);
|
|
316
|
+
}
|
|
317
|
+
case 'db2': {
|
|
318
|
+
const sign = amount < 0 ? '-' : '+';
|
|
319
|
+
const unitWord = `${unit.toUpperCase()}${Math.abs(amount) === 1 ? '' : 'S'}`;
|
|
320
|
+
return makeExpression(`(${e} ${sign} ${Math.abs(amount)} ${unitWord})`);
|
|
321
|
+
}
|
|
322
|
+
case 'snowflake': {
|
|
323
|
+
return makeExpression(`DATEADD(${unit}, ${amount}, ${e})`);
|
|
324
|
+
}
|
|
325
|
+
case 'clickhouse': {
|
|
326
|
+
// ClickHouse supports ANSI-style INTERVAL arithmetic directly.
|
|
327
|
+
const sign = amount < 0 ? '-' : '+';
|
|
328
|
+
return makeExpression(`${e} ${sign} INTERVAL ${Math.abs(amount)} ${unit.toUpperCase()}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// Fallback (unreachable for supported dialects).
|
|
332
|
+
return makeExpression(`${e} + INTERVAL '${amount} ${unit}'`);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Add `amount` units of time to a date/timestamp expression.
|
|
336
|
+
* See dialect notes on {@link dateTrunc} / module doc for the general
|
|
337
|
+
* per-dialect divergence rationale; per-function notes:
|
|
338
|
+
* - postgres/cockroachdb: `expr + INTERVAL 'n unit'`.
|
|
339
|
+
* - redshift: `DATEADD(unit, n, expr)` (SQL-Server-style function, NOT interval
|
|
340
|
+
* arithmetic, despite being Postgres-derived).
|
|
341
|
+
* - mysql/mariadb: `DATE_ADD(expr, INTERVAL n UNIT)`.
|
|
342
|
+
* - mssql/snowflake: `DATEADD(unit, n, expr)`.
|
|
343
|
+
* - oracle: day-level uses plain `+`/`-` arithmetic (Oracle DATE math is in
|
|
344
|
+
* days); month/year use `ADD_MONTHS`; hour/minute/second are expressed as
|
|
345
|
+
* fractional-day arithmetic.
|
|
346
|
+
* - sqlite: `datetime(expr, '+n units')`.
|
|
347
|
+
* - db2: labeled-duration arithmetic, e.g. `expr + n DAYS`.
|
|
348
|
+
* - clickhouse: ANSI `expr + INTERVAL n UNIT`.
|
|
349
|
+
*/
|
|
350
|
+
function dateAdd(dialect, expr, amount, unit) {
|
|
351
|
+
return dateAddInternal(dialect, expr, amount, unit);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Subtract `amount` units of time from a date/timestamp expression.
|
|
355
|
+
* Equivalent to `dateAdd(dialect, expr, -amount, unit)` — see {@link dateAdd}
|
|
356
|
+
* for per-dialect SQL notes.
|
|
357
|
+
*/
|
|
358
|
+
function dateSub(dialect, expr, amount, unit) {
|
|
359
|
+
return dateAddInternal(dialect, expr, -amount, unit);
|
|
360
|
+
}
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// dateDiff
|
|
363
|
+
// ============================================================================
|
|
364
|
+
/**
|
|
365
|
+
* Compute the difference between two date/timestamp expressions
|
|
366
|
+
* (`end - start`) expressed in the given unit.
|
|
367
|
+
*
|
|
368
|
+
* Dialect notes (this is the function with the widest genuine divergence):
|
|
369
|
+
* - mysql/mariadb: `TIMESTAMPDIFF(UNIT, start, end)` (native, all units).
|
|
370
|
+
* - mssql/snowflake: `DATEDIFF(unit, start, end)` (native, all units).
|
|
371
|
+
* - redshift: also has a genuine native `DATEDIFF(datepart, start, end)`
|
|
372
|
+
* function (SQL-Server-style), unlike Postgres which has no such function.
|
|
373
|
+
* - clickhouse: native `dateDiff('unit', start, end)`.
|
|
374
|
+
* - postgres/cockroachdb: NO native DATEDIFF — day uses date subtraction
|
|
375
|
+
* (`end::date - start::date`), sub-day units use
|
|
376
|
+
* `EXTRACT(EPOCH FROM (end - start))` scaled, and year/month are computed
|
|
377
|
+
* from `EXTRACT(YEAR/MONTH FROM ...)` components.
|
|
378
|
+
* - oracle: NO native DATEDIFF — DATE subtraction yields a day count
|
|
379
|
+
* (`TRUNC(end) - TRUNC(start)` for whole days, scaled for other sub-day
|
|
380
|
+
* units); month/year use `MONTHS_BETWEEN`.
|
|
381
|
+
* - sqlite: NO native DATEDIFF — built on `julianday()` differences (day and
|
|
382
|
+
* sub-day units) and `strftime('%Y'/'%m', ...)` component differences
|
|
383
|
+
* (month/year, since julianday-based month math is not calendar-exact).
|
|
384
|
+
* - db2: NO native DATEDIFF — built from `DAYS()` (whole-day difference) and
|
|
385
|
+
* `MIDNIGHT_SECONDS()` (intra-day seconds) for day/sub-day units, and
|
|
386
|
+
* `YEAR()`/`MONTH()` component differences for month/year.
|
|
387
|
+
*/
|
|
388
|
+
function dateDiff(dialect, unit, start, end) {
|
|
389
|
+
const s = toSqlFragment(start);
|
|
390
|
+
const e = toSqlFragment(end);
|
|
391
|
+
switch (dialect) {
|
|
392
|
+
case 'mysql':
|
|
393
|
+
case 'mariadb': {
|
|
394
|
+
return makeExpression(`TIMESTAMPDIFF(${unit.toUpperCase()}, ${s}, ${e})`);
|
|
395
|
+
}
|
|
396
|
+
case 'mssql':
|
|
397
|
+
case 'snowflake':
|
|
398
|
+
case 'redshift': {
|
|
399
|
+
return makeExpression(`DATEDIFF(${unit}, ${s}, ${e})`);
|
|
400
|
+
}
|
|
401
|
+
case 'clickhouse': {
|
|
402
|
+
return makeExpression(`dateDiff('${unit}', ${s}, ${e})`);
|
|
403
|
+
}
|
|
404
|
+
case 'postgres':
|
|
405
|
+
case 'cockroachdb': {
|
|
406
|
+
switch (unit) {
|
|
407
|
+
case 'year':
|
|
408
|
+
return makeExpression(`(EXTRACT(YEAR FROM ${e}) - EXTRACT(YEAR FROM ${s}))`);
|
|
409
|
+
case 'quarter':
|
|
410
|
+
return makeExpression(`(((EXTRACT(YEAR FROM ${e}) - EXTRACT(YEAR FROM ${s})) * 4) + (EXTRACT(QUARTER FROM ${e}) - EXTRACT(QUARTER FROM ${s})))`);
|
|
411
|
+
case 'month':
|
|
412
|
+
return makeExpression(`(((EXTRACT(YEAR FROM ${e}) - EXTRACT(YEAR FROM ${s})) * 12) + (EXTRACT(MONTH FROM ${e}) - EXTRACT(MONTH FROM ${s})))`);
|
|
413
|
+
case 'week':
|
|
414
|
+
return makeExpression(`FLOOR((CAST(${e} AS DATE) - CAST(${s} AS DATE)) / 7)`);
|
|
415
|
+
case 'day':
|
|
416
|
+
return makeExpression(`(CAST(${e} AS DATE) - CAST(${s} AS DATE))`);
|
|
417
|
+
case 'hour':
|
|
418
|
+
return makeExpression(`(EXTRACT(EPOCH FROM (${e} - ${s})) / 3600)`);
|
|
419
|
+
case 'minute':
|
|
420
|
+
return makeExpression(`(EXTRACT(EPOCH FROM (${e} - ${s})) / 60)`);
|
|
421
|
+
case 'second':
|
|
422
|
+
return makeExpression(`EXTRACT(EPOCH FROM (${e} - ${s}))`);
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
case 'oracle': {
|
|
427
|
+
switch (unit) {
|
|
428
|
+
case 'year':
|
|
429
|
+
return makeExpression(`(MONTHS_BETWEEN(${e}, ${s}) / 12)`);
|
|
430
|
+
case 'quarter':
|
|
431
|
+
return makeExpression(`(MONTHS_BETWEEN(${e}, ${s}) / 3)`);
|
|
432
|
+
case 'month':
|
|
433
|
+
return makeExpression(`MONTHS_BETWEEN(${e}, ${s})`);
|
|
434
|
+
case 'week':
|
|
435
|
+
return makeExpression(`((${e} - ${s}) / 7)`);
|
|
436
|
+
case 'day':
|
|
437
|
+
return makeExpression(`(TRUNC(${e}) - TRUNC(${s}))`);
|
|
438
|
+
case 'hour':
|
|
439
|
+
return makeExpression(`((${e} - ${s}) * 24)`);
|
|
440
|
+
case 'minute':
|
|
441
|
+
return makeExpression(`((${e} - ${s}) * 1440)`);
|
|
442
|
+
case 'second':
|
|
443
|
+
return makeExpression(`((${e} - ${s}) * 86400)`);
|
|
444
|
+
}
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
case 'sqlite': {
|
|
448
|
+
switch (unit) {
|
|
449
|
+
case 'year':
|
|
450
|
+
return makeExpression(`(CAST(strftime('%Y', ${e}) AS INTEGER) - CAST(strftime('%Y', ${s}) AS INTEGER))`);
|
|
451
|
+
case 'quarter':
|
|
452
|
+
return makeExpression(`(((CAST(strftime('%Y', ${e}) AS INTEGER) - CAST(strftime('%Y', ${s}) AS INTEGER)) * 4) + ((CAST(strftime('%m', ${e}) AS INTEGER) - 1) / 3 - (CAST(strftime('%m', ${s}) AS INTEGER) - 1) / 3))`);
|
|
453
|
+
case 'month':
|
|
454
|
+
return makeExpression(`(((CAST(strftime('%Y', ${e}) AS INTEGER) - CAST(strftime('%Y', ${s}) AS INTEGER)) * 12) + (CAST(strftime('%m', ${e}) AS INTEGER) - CAST(strftime('%m', ${s}) AS INTEGER)))`);
|
|
455
|
+
case 'week':
|
|
456
|
+
return makeExpression(`CAST((julianday(${e}) - julianday(${s})) / 7 AS INTEGER)`);
|
|
457
|
+
case 'day':
|
|
458
|
+
return makeExpression(`CAST(julianday(${e}) - julianday(${s}) AS INTEGER)`);
|
|
459
|
+
case 'hour':
|
|
460
|
+
return makeExpression(`((julianday(${e}) - julianday(${s})) * 24)`);
|
|
461
|
+
case 'minute':
|
|
462
|
+
return makeExpression(`((julianday(${e}) - julianday(${s})) * 1440)`);
|
|
463
|
+
case 'second':
|
|
464
|
+
return makeExpression(`((julianday(${e}) - julianday(${s})) * 86400)`);
|
|
465
|
+
}
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case 'db2': {
|
|
469
|
+
const totalSeconds = `((DAYS(${e}) - DAYS(${s})) * 86400 + (MIDNIGHT_SECONDS(${e}) - MIDNIGHT_SECONDS(${s})))`;
|
|
470
|
+
switch (unit) {
|
|
471
|
+
case 'year':
|
|
472
|
+
return makeExpression(`(YEAR(${e}) - YEAR(${s}))`);
|
|
473
|
+
case 'quarter':
|
|
474
|
+
return makeExpression(`(((YEAR(${e}) - YEAR(${s})) * 4) + (QUARTER(${e}) - QUARTER(${s})))`);
|
|
475
|
+
case 'month':
|
|
476
|
+
return makeExpression(`(((YEAR(${e}) - YEAR(${s})) * 12) + (MONTH(${e}) - MONTH(${s})))`);
|
|
477
|
+
case 'week':
|
|
478
|
+
return makeExpression(`((DAYS(${e}) - DAYS(${s})) / 7)`);
|
|
479
|
+
case 'day':
|
|
480
|
+
return makeExpression(`(DAYS(${e}) - DAYS(${s}))`);
|
|
481
|
+
case 'hour':
|
|
482
|
+
return makeExpression(`(${totalSeconds} / 3600)`);
|
|
483
|
+
case 'minute':
|
|
484
|
+
return makeExpression(`(${totalSeconds} / 60)`);
|
|
485
|
+
case 'second':
|
|
486
|
+
return makeExpression(totalSeconds);
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
// Fallback (unreachable for supported dialects).
|
|
492
|
+
return makeExpression(`DATEDIFF(${unit}, ${s}, ${e})`);
|
|
493
|
+
}
|
|
494
|
+
// ============================================================================
|
|
495
|
+
// now / currentDate / currentTimestamp
|
|
496
|
+
// ============================================================================
|
|
497
|
+
/**
|
|
498
|
+
* Current date-and-time (instant), dialect-appropriate.
|
|
499
|
+
* - mysql/mariadb: `NOW()`
|
|
500
|
+
* - postgres/cockroachdb/redshift: `NOW()`
|
|
501
|
+
* - mssql: `GETDATE()`
|
|
502
|
+
* - oracle: `SYSTIMESTAMP`
|
|
503
|
+
* - sqlite: `datetime('now')`
|
|
504
|
+
* - db2: `CURRENT TIMESTAMP` (special register, no parentheses)
|
|
505
|
+
* - snowflake: `CURRENT_TIMESTAMP()`
|
|
506
|
+
* - clickhouse: `now()`
|
|
507
|
+
*/
|
|
508
|
+
function now(dialect) {
|
|
509
|
+
switch (dialect) {
|
|
510
|
+
case 'mysql':
|
|
511
|
+
case 'mariadb':
|
|
512
|
+
case 'postgres':
|
|
513
|
+
case 'cockroachdb':
|
|
514
|
+
case 'redshift':
|
|
515
|
+
return makeExpression('NOW()');
|
|
516
|
+
case 'mssql':
|
|
517
|
+
return makeExpression('GETDATE()');
|
|
518
|
+
case 'oracle':
|
|
519
|
+
return makeExpression('SYSTIMESTAMP');
|
|
520
|
+
case 'sqlite':
|
|
521
|
+
return makeExpression(`datetime('now')`);
|
|
522
|
+
case 'db2':
|
|
523
|
+
return makeExpression('CURRENT TIMESTAMP');
|
|
524
|
+
case 'snowflake':
|
|
525
|
+
return makeExpression('CURRENT_TIMESTAMP()');
|
|
526
|
+
case 'clickhouse':
|
|
527
|
+
return makeExpression('now()');
|
|
528
|
+
}
|
|
529
|
+
return makeExpression('NOW()');
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Current date (no time component), dialect-appropriate.
|
|
533
|
+
* - mysql/mariadb: `CURDATE()`
|
|
534
|
+
* - postgres/cockroachdb/redshift: `CURRENT_DATE`
|
|
535
|
+
* - mssql: `CAST(GETDATE() AS DATE)`
|
|
536
|
+
* - oracle: `TRUNC(SYSDATE)`
|
|
537
|
+
* - sqlite: `date('now')`
|
|
538
|
+
* - db2: `CURRENT DATE` (special register)
|
|
539
|
+
* - snowflake: `CURRENT_DATE()`
|
|
540
|
+
* - clickhouse: `today()`
|
|
541
|
+
*/
|
|
542
|
+
function currentDate(dialect) {
|
|
543
|
+
switch (dialect) {
|
|
544
|
+
case 'mysql':
|
|
545
|
+
case 'mariadb':
|
|
546
|
+
return makeExpression('CURDATE()');
|
|
547
|
+
case 'postgres':
|
|
548
|
+
case 'cockroachdb':
|
|
549
|
+
case 'redshift':
|
|
550
|
+
return makeExpression('CURRENT_DATE');
|
|
551
|
+
case 'mssql':
|
|
552
|
+
return makeExpression('CAST(GETDATE() AS DATE)');
|
|
553
|
+
case 'oracle':
|
|
554
|
+
return makeExpression('TRUNC(SYSDATE)');
|
|
555
|
+
case 'sqlite':
|
|
556
|
+
return makeExpression(`date('now')`);
|
|
557
|
+
case 'db2':
|
|
558
|
+
return makeExpression('CURRENT DATE');
|
|
559
|
+
case 'snowflake':
|
|
560
|
+
return makeExpression('CURRENT_DATE()');
|
|
561
|
+
case 'clickhouse':
|
|
562
|
+
return makeExpression('today()');
|
|
563
|
+
}
|
|
564
|
+
return makeExpression('CURRENT_DATE');
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Current timestamp (ANSI-standard spelling where available), dialect-appropriate.
|
|
568
|
+
* - mysql/mariadb/postgres/cockroachdb/redshift/db2: `CURRENT_TIMESTAMP`
|
|
569
|
+
* - mssql: `CURRENT_TIMESTAMP` (ANSI synonym for `GETDATE()`)
|
|
570
|
+
* - oracle: `CURRENT_TIMESTAMP` (session-timezone-aware; distinct from `SYSTIMESTAMP`)
|
|
571
|
+
* - sqlite: `CURRENT_TIMESTAMP` (SQLite keyword, UTC)
|
|
572
|
+
* - snowflake: `CURRENT_TIMESTAMP()`
|
|
573
|
+
* - clickhouse: `now()` (no distinct `CURRENT_TIMESTAMP`; `now()` is the canonical form)
|
|
574
|
+
*/
|
|
575
|
+
function currentTimestamp(dialect) {
|
|
576
|
+
switch (dialect) {
|
|
577
|
+
case 'snowflake':
|
|
578
|
+
return makeExpression('CURRENT_TIMESTAMP()');
|
|
579
|
+
case 'clickhouse':
|
|
580
|
+
return makeExpression('now()');
|
|
581
|
+
default:
|
|
582
|
+
return makeExpression('CURRENT_TIMESTAMP');
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// ============================================================================
|
|
586
|
+
// formatDate
|
|
587
|
+
// ============================================================================
|
|
588
|
+
/**
|
|
589
|
+
* Format a date/timestamp expression as a string.
|
|
590
|
+
*
|
|
591
|
+
* IMPORTANT: format-token translation here is BEST-EFFORT for a common
|
|
592
|
+
* subset of tokens, NOT a full strftime-compatible formatting engine. Only
|
|
593
|
+
* the following tokens are recognized in the input `format` string (case
|
|
594
|
+
* sensitive, matched longest-first so e.g. `HH24` is not partially consumed
|
|
595
|
+
* by `HH`):
|
|
596
|
+
* - `YYYY` - 4-digit year
|
|
597
|
+
* - `MM` - 2-digit month (01-12)
|
|
598
|
+
* - `DD` - 2-digit day of month (01-31)
|
|
599
|
+
* - `HH24` - 2-digit hour, 24-hour clock (00-23)
|
|
600
|
+
* - `HH` - 2-digit hour, 12-hour clock (01-12)
|
|
601
|
+
* - `MI` - 2-digit minute (00-59)
|
|
602
|
+
* - `SS` - 2-digit second (00-59)
|
|
603
|
+
* Any other characters in `format` (literal separators like `-`, `/`, `:`,
|
|
604
|
+
* spaces, etc) are passed through unchanged, best-effort, per dialect.
|
|
605
|
+
*
|
|
606
|
+
* Dialect notes:
|
|
607
|
+
* - postgres/cockroachdb/redshift/oracle: `TO_CHAR(expr, format)` — these
|
|
608
|
+
* tokens map onto TO_CHAR's native template natively, so the format string
|
|
609
|
+
* passes through unchanged.
|
|
610
|
+
* - mysql/mariadb: `DATE_FORMAT(expr, format)`, tokens translated to `%Y`,
|
|
611
|
+
* `%m`, `%d`, `%H`, `%h`, `%i`, `%s`.
|
|
612
|
+
* - mssql: `FORMAT(expr, format)`, tokens translated to .NET-style `yyyy`,
|
|
613
|
+
* `MM`, `dd`, `HH`, `hh`, `mm`, `ss`.
|
|
614
|
+
* - sqlite: `strftime(format, expr)`, tokens translated to `%Y`, `%m`, `%d`,
|
|
615
|
+
* `%H`, `%I`, `%M`, `%S`.
|
|
616
|
+
* - db2: `VARCHAR_FORMAT(expr, format)` (Db2's TO_CHAR-equivalent); tokens
|
|
617
|
+
* pass through unchanged like Oracle/Postgres.
|
|
618
|
+
* - snowflake: `TO_VARCHAR(expr, format)`; tokens pass through unchanged.
|
|
619
|
+
* - clickhouse: `formatDateTime(expr, format)`, tokens translated to `%Y`,
|
|
620
|
+
* `%m`, `%d`, `%H`, `%I`, `%i`, `%S` (ClickHouse's directives are
|
|
621
|
+
* MySQL-`strftime`-like, notably using `%i` for minutes like MySQL).
|
|
622
|
+
*/
|
|
623
|
+
function formatDate(dialect, expr, format) {
|
|
624
|
+
const e = toSqlFragment(expr);
|
|
625
|
+
const translate = (tokenMap) => {
|
|
626
|
+
// Replace longest tokens first (HH24 before HH) to avoid partial matches.
|
|
627
|
+
const tokens = Object.keys(tokenMap).sort((a, b) => b.length - a.length);
|
|
628
|
+
const pattern = new RegExp(tokens.join('|'), 'g');
|
|
629
|
+
return format.replace(pattern, (match) => tokenMap[match]);
|
|
630
|
+
};
|
|
631
|
+
switch (dialect) {
|
|
632
|
+
case 'postgres':
|
|
633
|
+
case 'cockroachdb':
|
|
634
|
+
case 'redshift':
|
|
635
|
+
case 'oracle': {
|
|
636
|
+
return makeExpression(`TO_CHAR(${e}, '${format}')`);
|
|
637
|
+
}
|
|
638
|
+
case 'mysql':
|
|
639
|
+
case 'mariadb': {
|
|
640
|
+
const translated = translate({
|
|
641
|
+
YYYY: '%Y',
|
|
642
|
+
MM: '%m',
|
|
643
|
+
DD: '%d',
|
|
644
|
+
HH24: '%H',
|
|
645
|
+
HH: '%h',
|
|
646
|
+
MI: '%i',
|
|
647
|
+
SS: '%s',
|
|
648
|
+
});
|
|
649
|
+
return makeExpression(`DATE_FORMAT(${e}, '${translated}')`);
|
|
650
|
+
}
|
|
651
|
+
case 'mssql': {
|
|
652
|
+
const translated = translate({
|
|
653
|
+
YYYY: 'yyyy',
|
|
654
|
+
MM: 'MM',
|
|
655
|
+
DD: 'dd',
|
|
656
|
+
HH24: 'HH',
|
|
657
|
+
HH: 'hh',
|
|
658
|
+
MI: 'mm',
|
|
659
|
+
SS: 'ss',
|
|
660
|
+
});
|
|
661
|
+
return makeExpression(`FORMAT(${e}, '${translated}')`);
|
|
662
|
+
}
|
|
663
|
+
case 'sqlite': {
|
|
664
|
+
const translated = translate({
|
|
665
|
+
YYYY: '%Y',
|
|
666
|
+
MM: '%m',
|
|
667
|
+
DD: '%d',
|
|
668
|
+
HH24: '%H',
|
|
669
|
+
HH: '%I',
|
|
670
|
+
MI: '%M',
|
|
671
|
+
SS: '%S',
|
|
672
|
+
});
|
|
673
|
+
return makeExpression(`strftime('${translated}', ${e})`);
|
|
674
|
+
}
|
|
675
|
+
case 'db2': {
|
|
676
|
+
return makeExpression(`VARCHAR_FORMAT(${e}, '${format}')`);
|
|
677
|
+
}
|
|
678
|
+
case 'snowflake': {
|
|
679
|
+
return makeExpression(`TO_VARCHAR(${e}, '${format}')`);
|
|
680
|
+
}
|
|
681
|
+
case 'clickhouse': {
|
|
682
|
+
const translated = translate({
|
|
683
|
+
YYYY: '%Y',
|
|
684
|
+
MM: '%m',
|
|
685
|
+
DD: '%d',
|
|
686
|
+
HH24: '%H',
|
|
687
|
+
HH: '%I',
|
|
688
|
+
MI: '%i',
|
|
689
|
+
SS: '%S',
|
|
690
|
+
});
|
|
691
|
+
return makeExpression(`formatDateTime(${e}, '${translated}')`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return makeExpression(`TO_CHAR(${e}, '${format}')`);
|
|
695
|
+
}
|