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,556 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Query options and performance hint decorators for the ORM.
|
|
4
|
+
*
|
|
5
|
+
* Provides decorators for controlling query execution:
|
|
6
|
+
* - Query hints (index hints, force index, ignore index)
|
|
7
|
+
* - Lock options (row locking, timeout, skip locked)
|
|
8
|
+
* - Statement timeout
|
|
9
|
+
* - Idle in transaction timeout
|
|
10
|
+
* - Cursor-based fetching
|
|
11
|
+
* - FETCH options (FIRST, OFFSET)
|
|
12
|
+
* - DISTINCT ON (PostgreSQL)
|
|
13
|
+
* - TABLESAMPLE
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
*
|
|
17
|
+
* // Index hints
|
|
18
|
+
* @UseIndex('idx_name')
|
|
19
|
+
* @ForceIndex('idx_name')
|
|
20
|
+
* @IgnoreIndex('idx_name')
|
|
21
|
+
* class User extends Model {}
|
|
22
|
+
*
|
|
23
|
+
* // Lock options (method decorator)
|
|
24
|
+
* @WithLock({ mode: 'UPDATE', timeout: 5000, skipLocked: true })
|
|
25
|
+
* async processOrder(id: number) { ... }
|
|
26
|
+
*
|
|
27
|
+
* // Statement timeout (method decorator)
|
|
28
|
+
* @StatementTimeout(30000)
|
|
29
|
+
* async runLongQuery() { ... }
|
|
30
|
+
*
|
|
31
|
+
* // Cursor-based fetch
|
|
32
|
+
* @Cursor({ holdable: true })
|
|
33
|
+
* async *getAllUsers() { ... }
|
|
34
|
+
*
|
|
35
|
+
* // FETCH options
|
|
36
|
+
* @Fetch(100)
|
|
37
|
+
* @Distinct
|
|
38
|
+
* async *getUsers() { ... }
|
|
39
|
+
*
|
|
40
|
+
* // TABLESAMPLE
|
|
41
|
+
* @Tablesample({ method: 'BERNOULLI', percentage: 50 })
|
|
42
|
+
* async *getSampleUsers() { ... }
|
|
43
|
+
*/
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.DistinctOn = void 0;
|
|
46
|
+
exports.UseIndex = UseIndex;
|
|
47
|
+
exports.ForceIndex = ForceIndex;
|
|
48
|
+
exports.IgnoreIndex = IgnoreIndex;
|
|
49
|
+
exports.Tablesample = Tablesample;
|
|
50
|
+
exports.Distinct = Distinct;
|
|
51
|
+
exports.WithLock = WithLock;
|
|
52
|
+
exports.StatementTimeout = StatementTimeout;
|
|
53
|
+
exports.IdleInTransactionTimeout = IdleInTransactionTimeout;
|
|
54
|
+
exports.Cursor = Cursor;
|
|
55
|
+
exports.Fetch = Fetch;
|
|
56
|
+
exports.FetchWith = FetchWith;
|
|
57
|
+
exports.getQueryHints = getQueryHints;
|
|
58
|
+
exports.getLockOptions = getLockOptions;
|
|
59
|
+
exports.getStatementTimeout = getStatementTimeout;
|
|
60
|
+
exports.getIdleInTransactionTimeout = getIdleInTransactionTimeout;
|
|
61
|
+
exports.getCursorOptions = getCursorOptions;
|
|
62
|
+
exports.getFetchOptions = getFetchOptions;
|
|
63
|
+
exports.getDistinctOnColumns = getDistinctOnColumns;
|
|
64
|
+
exports.getTablesampleOptions = getTablesampleOptions;
|
|
65
|
+
exports.isDistinctEnabled = isDistinctEnabled;
|
|
66
|
+
exports.generateIndexHintSQL = generateIndexHintSQL;
|
|
67
|
+
exports.generateLockSQL = generateLockSQL;
|
|
68
|
+
exports.generateStatementTimeoutSQL = generateStatementTimeoutSQL;
|
|
69
|
+
exports.generateIdleInTransactionTimeoutSQL = generateIdleInTransactionTimeoutSQL;
|
|
70
|
+
exports.generateTablesampleSQL = generateTablesampleSQL;
|
|
71
|
+
exports.generateDistinctOnSQL = generateDistinctOnSQL;
|
|
72
|
+
exports.generateCursorSQL = generateCursorSQL;
|
|
73
|
+
require("reflect-metadata");
|
|
74
|
+
// ==================== Metadata Keys ====================
|
|
75
|
+
const QUERY_HINTS_KEY = Symbol('prorm:queryHints');
|
|
76
|
+
const LOCK_OPTIONS_KEY = Symbol('prorm:lockOptions');
|
|
77
|
+
const STATEMENT_TIMEOUT_KEY = Symbol('prorm:statementTimeout');
|
|
78
|
+
const IDLE_TRANSACTION_TIMEOUT_KEY = Symbol('prorm:idleInTransactionTimeout');
|
|
79
|
+
const CURSOR_OPTIONS_KEY = Symbol('prorm:cursorOptions');
|
|
80
|
+
const FETCH_OPTIONS_KEY = Symbol('prorm:fetchOptions');
|
|
81
|
+
const DISTINCT_ON_KEY = Symbol('prorm:distinctOn');
|
|
82
|
+
const TABLESAMPLE_KEY = Symbol('prorm:tablesample');
|
|
83
|
+
const DISTINCT_KEY = Symbol('prorm:distinct');
|
|
84
|
+
// ==================== Class Decorators ====================
|
|
85
|
+
/**
|
|
86
|
+
* Decorator to specify USE INDEX hint.
|
|
87
|
+
* Suggest the optimizer to use a specific index.
|
|
88
|
+
*
|
|
89
|
+
* Usage:
|
|
90
|
+
*
|
|
91
|
+
* @UseIndex('idx_user_email')
|
|
92
|
+
* @UseIndex(['idx_user_email', 'idx_user_status'])
|
|
93
|
+
* class User extends Model {}
|
|
94
|
+
*
|
|
95
|
+
* @param index - Index name or array of index names
|
|
96
|
+
*/
|
|
97
|
+
function UseIndex(index) {
|
|
98
|
+
return function (target) {
|
|
99
|
+
const indexes = Array.isArray(index) ? index : [index];
|
|
100
|
+
const existing = Reflect.getMetadata(QUERY_HINTS_KEY, target) || {};
|
|
101
|
+
existing.useIndex = [...(existing.useIndex || []), ...indexes];
|
|
102
|
+
Reflect.defineMetadata(QUERY_HINTS_KEY, existing, target);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Decorator to specify FORCE INDEX hint.
|
|
107
|
+
* Force the optimizer to use a specific index.
|
|
108
|
+
*
|
|
109
|
+
* Usage:
|
|
110
|
+
*
|
|
111
|
+
* @ForceIndex('idx_user_email')
|
|
112
|
+
* class User extends Model {}
|
|
113
|
+
*
|
|
114
|
+
* @param index - Index name
|
|
115
|
+
*/
|
|
116
|
+
function ForceIndex(index) {
|
|
117
|
+
return function (target) {
|
|
118
|
+
const indexes = Array.isArray(index) ? index : [index];
|
|
119
|
+
const existing = Reflect.getMetadata(QUERY_HINTS_KEY, target) || {};
|
|
120
|
+
existing.forceIndex = [...(existing.forceIndex || []), ...indexes];
|
|
121
|
+
Reflect.defineMetadata(QUERY_HINTS_KEY, existing, target);
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Decorator to specify IGNORE INDEX hint.
|
|
126
|
+
* Tell the optimizer to ignore a specific index.
|
|
127
|
+
*
|
|
128
|
+
* Usage:
|
|
129
|
+
*
|
|
130
|
+
* @IgnoreIndex('idx_user_email')
|
|
131
|
+
* class User extends Model {}
|
|
132
|
+
*
|
|
133
|
+
* @param index - Index name
|
|
134
|
+
*/
|
|
135
|
+
function IgnoreIndex(index) {
|
|
136
|
+
return function (target) {
|
|
137
|
+
const indexes = Array.isArray(index) ? index : [index];
|
|
138
|
+
const existing = Reflect.getMetadata(QUERY_HINTS_KEY, target) || {};
|
|
139
|
+
existing.ignoreIndex = [...(existing.ignoreIndex || []), ...indexes];
|
|
140
|
+
Reflect.defineMetadata(QUERY_HINTS_KEY, existing, target);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Decorator to set tablesample for query.
|
|
145
|
+
* Sample a subset of rows for query performance.
|
|
146
|
+
*
|
|
147
|
+
* Usage:
|
|
148
|
+
*
|
|
149
|
+
* @Tablesample({ method: 'BERNOULLI', percentage: 50 })
|
|
150
|
+
* class User extends Model {}
|
|
151
|
+
*
|
|
152
|
+
* @Tablesample({ method: 'SYSTEM', pages: 100 })
|
|
153
|
+
* class User extends Model {}
|
|
154
|
+
*
|
|
155
|
+
* @param options - Tablesample options
|
|
156
|
+
*/
|
|
157
|
+
function Tablesample(options) {
|
|
158
|
+
return function (target) {
|
|
159
|
+
Reflect.defineMetadata(TABLESAMPLE_KEY, options, target);
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Decorator to enable DISTINCT ON (PostgreSQL).
|
|
164
|
+
*
|
|
165
|
+
* Usage:
|
|
166
|
+
*
|
|
167
|
+
* @Distinct
|
|
168
|
+
* class User extends Model {}
|
|
169
|
+
*
|
|
170
|
+
* @DistinctOn(['email'])
|
|
171
|
+
* class User extends Model {}
|
|
172
|
+
*
|
|
173
|
+
* @param columns - Columns for DISTINCT ON (optional)
|
|
174
|
+
*/
|
|
175
|
+
function Distinct(columns) {
|
|
176
|
+
return function (target) {
|
|
177
|
+
if (columns?.length) {
|
|
178
|
+
Reflect.defineMetadata(DISTINCT_ON_KEY, columns, target);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
Reflect.defineMetadata(DISTINCT_KEY, true, target);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Alias for @Distinct for DISTINCT ON
|
|
187
|
+
*/
|
|
188
|
+
exports.DistinctOn = Distinct;
|
|
189
|
+
// ==================== Method Decorators ====================
|
|
190
|
+
/**
|
|
191
|
+
* Decorator to apply row lock to a method.
|
|
192
|
+
* Provides options for locking rows during query execution.
|
|
193
|
+
*
|
|
194
|
+
* Usage:
|
|
195
|
+
*
|
|
196
|
+
* @WithLock({ mode: 'UPDATE', timeout: 5000, skipLocked: true })
|
|
197
|
+
* async processOrder(id: number) {
|
|
198
|
+
* const order = await Order.findByPk(id, { lock: 'UPDATE' });
|
|
199
|
+
* // process order
|
|
200
|
+
* }
|
|
201
|
+
*
|
|
202
|
+
* @param options - Lock options
|
|
203
|
+
*/
|
|
204
|
+
function WithLock(options = {}) {
|
|
205
|
+
return function (target, propertyKey, descriptor) {
|
|
206
|
+
const lockOptions = {
|
|
207
|
+
mode: options.mode || 'UPDATE',
|
|
208
|
+
timeout: options.timeout || 30000,
|
|
209
|
+
skipLocked: options.skipLocked || false,
|
|
210
|
+
tables: options.tables,
|
|
211
|
+
};
|
|
212
|
+
Reflect.defineMetadata(LOCK_OPTIONS_KEY, lockOptions, target, propertyKey);
|
|
213
|
+
// Wrap the method to apply lock context
|
|
214
|
+
const originalMethod = descriptor.value;
|
|
215
|
+
descriptor.value = async function (...args) {
|
|
216
|
+
// Note: In practice, this would acquire the lock via prorm.transaction
|
|
217
|
+
// The lock is applied when queries are executed within the method
|
|
218
|
+
return originalMethod.apply(this, args);
|
|
219
|
+
};
|
|
220
|
+
return descriptor;
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Decorator to set statement timeout.
|
|
225
|
+
* Abort queries that take longer than the specified timeout.
|
|
226
|
+
*
|
|
227
|
+
* Usage:
|
|
228
|
+
*
|
|
229
|
+
* @StatementTimeout(30000) // 30 seconds
|
|
230
|
+
* async runLongQuery() {
|
|
231
|
+
* return await this.query('SELECT * FROM large_table');
|
|
232
|
+
* }
|
|
233
|
+
*
|
|
234
|
+
* @param timeout - Timeout in milliseconds
|
|
235
|
+
*/
|
|
236
|
+
function StatementTimeout(timeout) {
|
|
237
|
+
return function (target, propertyKey, descriptor) {
|
|
238
|
+
Reflect.defineMetadata(STATEMENT_TIMEOUT_KEY, { timeout, scope: 'session' }, target, propertyKey);
|
|
239
|
+
// Wrap the method to set timeout
|
|
240
|
+
const originalMethod = descriptor.value;
|
|
241
|
+
descriptor.value = async function (...args) {
|
|
242
|
+
// Note: In practice, this would set the timeout before executing
|
|
243
|
+
// and reset it after the method completes
|
|
244
|
+
return originalMethod.apply(this, args);
|
|
245
|
+
};
|
|
246
|
+
return descriptor;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Decorator to set idle in transaction timeout.
|
|
251
|
+
* Abort transactions that are idle for longer than the specified timeout.
|
|
252
|
+
*
|
|
253
|
+
* Usage:
|
|
254
|
+
*
|
|
255
|
+
* @IdleInTransactionTimeout(60000) // 1 minute
|
|
256
|
+
* async runTransaction() {
|
|
257
|
+
* // long running transaction
|
|
258
|
+
* }
|
|
259
|
+
*
|
|
260
|
+
* @param timeout - Timeout in milliseconds
|
|
261
|
+
*/
|
|
262
|
+
function IdleInTransactionTimeout(timeout) {
|
|
263
|
+
return function (target, propertyKey, descriptor) {
|
|
264
|
+
Reflect.defineMetadata(IDLE_TRANSACTION_TIMEOUT_KEY, { timeout, scope: 'session' }, target, propertyKey);
|
|
265
|
+
return descriptor;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Decorator to use cursor-based fetching.
|
|
270
|
+
* Useful for processing large result sets in batches.
|
|
271
|
+
*
|
|
272
|
+
* Usage:
|
|
273
|
+
*
|
|
274
|
+
* @Cursor({ holdable: true })
|
|
275
|
+
* async *getAllUsers() {
|
|
276
|
+
* for await (const user of this.findAll({ cursor: true })) {
|
|
277
|
+
* yield user;
|
|
278
|
+
* }
|
|
279
|
+
* }
|
|
280
|
+
*
|
|
281
|
+
* @param options - Cursor options
|
|
282
|
+
*/
|
|
283
|
+
function Cursor(options = {}) {
|
|
284
|
+
return function (target, propertyKey, descriptor) {
|
|
285
|
+
const cursorOptions = {
|
|
286
|
+
holdable: options.holdable || 'WITHOUT HOLD',
|
|
287
|
+
name: options.name,
|
|
288
|
+
scroll: options.scroll ?? true,
|
|
289
|
+
};
|
|
290
|
+
Reflect.defineMetadata(CURSOR_OPTIONS_KEY, cursorOptions, target, propertyKey);
|
|
291
|
+
return descriptor;
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Decorator to set FETCH FIRST limit.
|
|
296
|
+
* Limits the number of rows returned by a query.
|
|
297
|
+
*
|
|
298
|
+
* Usage:
|
|
299
|
+
*
|
|
300
|
+
* @Fetch(100)
|
|
301
|
+
* async *getTopUsers() {
|
|
302
|
+
* return await User.findAll({ limit: 100 });
|
|
303
|
+
* }
|
|
304
|
+
*
|
|
305
|
+
* @param limit - Number of rows to fetch
|
|
306
|
+
*/
|
|
307
|
+
function Fetch(limit) {
|
|
308
|
+
return function (target, propertyKey, descriptor) {
|
|
309
|
+
Reflect.defineMetadata(FETCH_OPTIONS_KEY, { limit, offset: 0 }, target, propertyKey);
|
|
310
|
+
return descriptor;
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Decorator to set FETCH with offset.
|
|
315
|
+
* Useful for pagination.
|
|
316
|
+
*
|
|
317
|
+
* Usage:
|
|
318
|
+
*
|
|
319
|
+
* @Fetch({ first: 100, offset: 50 })
|
|
320
|
+
* async *getPageUsers() {
|
|
321
|
+
* return await User.findAll({ offset: 50, limit: 100 });
|
|
322
|
+
* }
|
|
323
|
+
*
|
|
324
|
+
* @param options - Fetch options with limit and optional offset
|
|
325
|
+
*/
|
|
326
|
+
function FetchWith(options) {
|
|
327
|
+
return function (target, propertyKey, descriptor) {
|
|
328
|
+
Reflect.defineMetadata(FETCH_OPTIONS_KEY, { limit: options.first, offset: options.offset || 0 }, target, propertyKey);
|
|
329
|
+
return descriptor;
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
// ==================== Helper Functions ====================
|
|
333
|
+
/**
|
|
334
|
+
* Get query hints for a model.
|
|
335
|
+
*
|
|
336
|
+
* @param target - The target class or prototype
|
|
337
|
+
* @returns Query hints or undefined
|
|
338
|
+
*/
|
|
339
|
+
function getQueryHints(target) {
|
|
340
|
+
return Reflect.getMetadata(QUERY_HINTS_KEY, target);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Get lock options for a method.
|
|
344
|
+
*
|
|
345
|
+
* @param target - The target object
|
|
346
|
+
* @param propertyKey - The property key
|
|
347
|
+
* @returns Lock options or undefined
|
|
348
|
+
*/
|
|
349
|
+
function getLockOptions(target, propertyKey) {
|
|
350
|
+
return Reflect.getMetadata(LOCK_OPTIONS_KEY, target, propertyKey);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Get statement timeout for a method.
|
|
354
|
+
*
|
|
355
|
+
* @param target - The target object
|
|
356
|
+
* @param propertyKey - The property key
|
|
357
|
+
* @returns Statement timeout options or undefined
|
|
358
|
+
*/
|
|
359
|
+
function getStatementTimeout(target, propertyKey) {
|
|
360
|
+
return Reflect.getMetadata(STATEMENT_TIMEOUT_KEY, target, propertyKey);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Get idle transaction timeout for a method.
|
|
364
|
+
*
|
|
365
|
+
* @param target - The target object
|
|
366
|
+
* @param propertyKey - The property key
|
|
367
|
+
* @returns Idle transaction timeout options or undefined
|
|
368
|
+
*/
|
|
369
|
+
function getIdleInTransactionTimeout(target, propertyKey) {
|
|
370
|
+
return Reflect.getMetadata(IDLE_TRANSACTION_TIMEOUT_KEY, target, propertyKey);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Get cursor options for a method.
|
|
374
|
+
*
|
|
375
|
+
* @param target - The target object
|
|
376
|
+
* @param propertyKey - The property key
|
|
377
|
+
* @returns Cursor options or undefined
|
|
378
|
+
*/
|
|
379
|
+
function getCursorOptions(target, propertyKey) {
|
|
380
|
+
return Reflect.getMetadata(CURSOR_OPTIONS_KEY, target, propertyKey);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Get FETCH options for a method.
|
|
384
|
+
*
|
|
385
|
+
* @param target - The target object
|
|
386
|
+
* @param propertyKey - The property key
|
|
387
|
+
* @returns FETCH options or undefined
|
|
388
|
+
*/
|
|
389
|
+
function getFetchOptions(target, propertyKey) {
|
|
390
|
+
return Reflect.getMetadata(FETCH_OPTIONS_KEY, target, propertyKey);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get DISTINCT ON columns for a model.
|
|
394
|
+
*
|
|
395
|
+
* @param target - The target class or prototype
|
|
396
|
+
* @returns DISTINCT ON columns or undefined
|
|
397
|
+
*/
|
|
398
|
+
function getDistinctOnColumns(target) {
|
|
399
|
+
return Reflect.getMetadata(DISTINCT_ON_KEY, target);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Get tablesample options for a model.
|
|
403
|
+
*
|
|
404
|
+
* @param target - The target class or prototype
|
|
405
|
+
* @returns Tablesample options or undefined
|
|
406
|
+
*/
|
|
407
|
+
function getTablesampleOptions(target) {
|
|
408
|
+
return Reflect.getMetadata(TABLESAMPLE_KEY, target);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Check if DISTINCT is enabled for a model.
|
|
412
|
+
*
|
|
413
|
+
* @param target - The target class or prototype
|
|
414
|
+
* @returns True if DISTINCT is enabled
|
|
415
|
+
*/
|
|
416
|
+
function isDistinctEnabled(target) {
|
|
417
|
+
return Reflect.getMetadata(DISTINCT_KEY, target) === true;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Generate index hint SQL.
|
|
421
|
+
*
|
|
422
|
+
* @param hints - Query hints
|
|
423
|
+
* @param dialect - Database dialect
|
|
424
|
+
* @returns SQL string or undefined
|
|
425
|
+
*/
|
|
426
|
+
function generateIndexHintSQL(hints, dialect) {
|
|
427
|
+
const parts = [];
|
|
428
|
+
if (dialect === 'mysql' || dialect === 'mariadb') {
|
|
429
|
+
if (hints.useIndex?.length) {
|
|
430
|
+
parts.push(`USE INDEX (${hints.useIndex.join(', ')})`);
|
|
431
|
+
}
|
|
432
|
+
if (hints.forceIndex?.length) {
|
|
433
|
+
parts.push(`FORCE INDEX (${hints.forceIndex.join(', ')})`);
|
|
434
|
+
}
|
|
435
|
+
if (hints.ignoreIndex?.length) {
|
|
436
|
+
parts.push(`IGNORE INDEX (${hints.ignoreIndex.join(', ')})`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (dialect === 'sqlserver') {
|
|
440
|
+
if (hints.useIndex?.length) {
|
|
441
|
+
parts.push(`WITH (INDEX(${hints.useIndex.join(', ')}))`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return parts.length > 0 ? parts.join(' ') : undefined;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Generate lock SQL.
|
|
448
|
+
*
|
|
449
|
+
* @param options - Lock options
|
|
450
|
+
* @returns SQL FOR clause or undefined
|
|
451
|
+
*/
|
|
452
|
+
function generateLockSQL(options) {
|
|
453
|
+
if (options.mode === 'UPDATE') {
|
|
454
|
+
return 'FOR UPDATE' + (options.skipLocked ? ' SKIP LOCKED' : '');
|
|
455
|
+
}
|
|
456
|
+
if (options.mode === 'SHARE') {
|
|
457
|
+
return 'FOR SHARE' + (options.skipLocked ? ' SKIP LOCKED' : '');
|
|
458
|
+
}
|
|
459
|
+
if (options.mode === 'UPDATE NOWAIT') {
|
|
460
|
+
return 'FOR UPDATE NOWAIT';
|
|
461
|
+
}
|
|
462
|
+
if (options.mode === 'SHARE NOWAIT') {
|
|
463
|
+
return 'FOR SHARE NOWAIT';
|
|
464
|
+
}
|
|
465
|
+
if (options.mode === 'UPDATE SKIP LOCKED') {
|
|
466
|
+
return 'FOR UPDATE SKIP LOCKED';
|
|
467
|
+
}
|
|
468
|
+
return undefined;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Generate statement timeout SQL.
|
|
472
|
+
*
|
|
473
|
+
* @param timeout - Timeout in milliseconds
|
|
474
|
+
* @param scope - Session or global
|
|
475
|
+
* @returns SQL string
|
|
476
|
+
*/
|
|
477
|
+
function generateStatementTimeoutSQL(timeout, scope = 'session') {
|
|
478
|
+
if (timeout <= 0) {
|
|
479
|
+
return `SET ${scope} statement_timeout = 0`;
|
|
480
|
+
}
|
|
481
|
+
return `SET ${scope} statement_timeout = '${timeout}ms'`;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Generate idle in transaction timeout SQL.
|
|
485
|
+
*
|
|
486
|
+
* @param timeout - Timeout in milliseconds
|
|
487
|
+
* @param scope - Session or global
|
|
488
|
+
* @returns SQL string
|
|
489
|
+
*/
|
|
490
|
+
function generateIdleInTransactionTimeoutSQL(timeout, scope = 'session') {
|
|
491
|
+
return `SET ${scope} idle_in_transaction_session_timeout = '${timeout}ms'`;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Generate tablesample SQL.
|
|
495
|
+
*
|
|
496
|
+
* @param options - Tablesample options
|
|
497
|
+
* @returns SQL TABLESAMPLE clause
|
|
498
|
+
*/
|
|
499
|
+
function generateTablesampleSQL(options) {
|
|
500
|
+
const { method, percentage, pages, seed, repeatable } = options;
|
|
501
|
+
if (method === 'BERNOULLI') {
|
|
502
|
+
let sql = `TABLESAMPLE BERNOULLI(${percentage || 10})`;
|
|
503
|
+
if (repeatable !== undefined) {
|
|
504
|
+
sql += ` REPEATABLE(${repeatable})`;
|
|
505
|
+
}
|
|
506
|
+
else if (seed !== undefined) {
|
|
507
|
+
sql += ` REPEATABLE(${seed})`;
|
|
508
|
+
}
|
|
509
|
+
return sql;
|
|
510
|
+
}
|
|
511
|
+
if (method === 'SYSTEM') {
|
|
512
|
+
if (pages) {
|
|
513
|
+
let sql = `TABLESAMPLE SYSTEM(${pages})`;
|
|
514
|
+
if (repeatable !== undefined) {
|
|
515
|
+
sql += ` REPEATABLE(${repeatable})`;
|
|
516
|
+
}
|
|
517
|
+
return sql;
|
|
518
|
+
}
|
|
519
|
+
let sql = `TABLESAMPLE SYSTEM(${percentage || 10})`;
|
|
520
|
+
if (repeatable !== undefined) {
|
|
521
|
+
sql += ` REPEATABLE(${repeatable})`;
|
|
522
|
+
}
|
|
523
|
+
return sql;
|
|
524
|
+
}
|
|
525
|
+
if (method === 'SYSTEM_TIME') {
|
|
526
|
+
const timestamp = options.timestamp
|
|
527
|
+
? typeof options.timestamp === 'string'
|
|
528
|
+
? `'${options.timestamp}'`
|
|
529
|
+
: `'${options.timestamp.toISOString()}'`
|
|
530
|
+
: "timestamp 'now'";
|
|
531
|
+
return `TABLESAMPLE SYSTEM_TIME(${timestamp})`;
|
|
532
|
+
}
|
|
533
|
+
return '';
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Generate DISTINCT ON SQL (PostgreSQL).
|
|
537
|
+
*
|
|
538
|
+
* @param columns - DISTINCT ON columns
|
|
539
|
+
* @returns SQL DISTINCT ON clause
|
|
540
|
+
*/
|
|
541
|
+
function generateDistinctOnSQL(columns) {
|
|
542
|
+
return `DISTINCT ON (${columns.join(', ')})`;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Generate cursor SQL.
|
|
546
|
+
*
|
|
547
|
+
* @param options - Cursor options
|
|
548
|
+
* @param name - Cursor name (optional)
|
|
549
|
+
* @returns SQL DECLARE CURSOR statement
|
|
550
|
+
*/
|
|
551
|
+
function generateCursorSQL(options, name) {
|
|
552
|
+
const cursorName = name || options.name || 'cursor';
|
|
553
|
+
const hold = options.holdable === 'HOLD' ? 'WITH HOLD' : 'WITHOUT HOLD';
|
|
554
|
+
const scroll = options.scroll !== false ? 'SCROLL' : 'NO SCROLL';
|
|
555
|
+
return `DECLARE ${cursorName} CURSOR ${scroll} ${hold} FOR`;
|
|
556
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Range Decorator for the ORM
|
|
4
|
+
*
|
|
5
|
+
* Provides PostgreSQL Range Types support.
|
|
6
|
+
* Range types: int4range, int8range, numrange, tsrange, tstzrange, daterange
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
*
|
|
10
|
+
* // Timestamp range
|
|
11
|
+
* @Range('tsrange')
|
|
12
|
+
* duration: [Date, Date];
|
|
13
|
+
*
|
|
14
|
+
* // Integer range
|
|
15
|
+
* @Range('int4range')
|
|
16
|
+
* quantity: [number, number];
|
|
17
|
+
*
|
|
18
|
+
* // Date range
|
|
19
|
+
* @Range('daterange')
|
|
20
|
+
* dates: [string, string];
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.Range = Range;
|
|
24
|
+
exports.getRangeMetadata = getRangeMetadata;
|
|
25
|
+
exports.getAllRangeMetadata = getAllRangeMetadata;
|
|
26
|
+
exports.isRangeColumn = isRangeColumn;
|
|
27
|
+
exports.clearRangeMetadata = clearRangeMetadata;
|
|
28
|
+
exports.buildRangeLiteral = buildRangeLiteral;
|
|
29
|
+
exports.getRangeContainedBySQL = getRangeContainedBySQL;
|
|
30
|
+
exports.getRangeOverlapSQL = getRangeOverlapSQL;
|
|
31
|
+
// ==================== Registry ====================
|
|
32
|
+
const rangeMetadataMap = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Range decorator for PostgreSQL Range columns
|
|
35
|
+
*
|
|
36
|
+
* @param options - Required range options (subtype is required)
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Timestamp range
|
|
40
|
+
* class Reservation extends Model {
|
|
41
|
+
* @Range({ subtype: 'tsrange' })
|
|
42
|
+
* during: [Date, Date];
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* // Date range
|
|
46
|
+
* class Event extends Model {
|
|
47
|
+
* @Range({ subtype: 'daterange' })
|
|
48
|
+
* dates: [string, string];
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* // Integer range
|
|
52
|
+
* class Inventory extends Model {
|
|
53
|
+
* @Range({ subtype: 'int4range', allowNull: false })
|
|
54
|
+
* quantity: [number, number];
|
|
55
|
+
* }
|
|
56
|
+
*/
|
|
57
|
+
function Range(options) {
|
|
58
|
+
return function (target, propertyKey) {
|
|
59
|
+
const decoratorOptions = {
|
|
60
|
+
...options,
|
|
61
|
+
subtype: 'int4range',
|
|
62
|
+
allowNull: true,
|
|
63
|
+
};
|
|
64
|
+
// Validate subtype
|
|
65
|
+
const validSubtypes = [
|
|
66
|
+
'int4range',
|
|
67
|
+
'int8range',
|
|
68
|
+
'numrange',
|
|
69
|
+
'tsrange',
|
|
70
|
+
'tstzrange',
|
|
71
|
+
'daterange',
|
|
72
|
+
];
|
|
73
|
+
if (!validSubtypes.includes(decoratorOptions.subtype)) {
|
|
74
|
+
throw new Error(`Invalid range subtype: ${decoratorOptions.subtype}. Valid subtypes: ${validSubtypes.join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
// Get or create metadata map for this class
|
|
77
|
+
if (!rangeMetadataMap.has(target.constructor)) {
|
|
78
|
+
rangeMetadataMap.set(target.constructor, new Map());
|
|
79
|
+
}
|
|
80
|
+
const metadataMap = rangeMetadataMap.get(target.constructor);
|
|
81
|
+
// Store metadata
|
|
82
|
+
metadataMap.set(propertyKey, {
|
|
83
|
+
options: decoratorOptions,
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get Range metadata for a property
|
|
89
|
+
*
|
|
90
|
+
* @param target - The target class
|
|
91
|
+
* @param propertyKey - The property name
|
|
92
|
+
* @returns Range metadata or undefined if not found
|
|
93
|
+
*/
|
|
94
|
+
function getRangeMetadata(target, propertyKey) {
|
|
95
|
+
const metadataMap = rangeMetadataMap.get(target.constructor);
|
|
96
|
+
if (!metadataMap) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return metadataMap.get(propertyKey);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get all Range metadata for a class
|
|
103
|
+
*
|
|
104
|
+
* @param target - The target class
|
|
105
|
+
* @returns Map of property names to Range metadata
|
|
106
|
+
*/
|
|
107
|
+
function getAllRangeMetadata(target) {
|
|
108
|
+
return rangeMetadataMap.get(target.constructor) || new Map();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if a property is a Range column
|
|
112
|
+
*
|
|
113
|
+
* @param target - The target class
|
|
114
|
+
* @param propertyKey - The property name
|
|
115
|
+
* @returns True if the property is a Range column
|
|
116
|
+
*/
|
|
117
|
+
function isRangeColumn(target, propertyKey) {
|
|
118
|
+
const metadata = getRangeMetadata(target, propertyKey);
|
|
119
|
+
return metadata !== undefined;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Clear Range metadata (useful for testing)
|
|
123
|
+
*/
|
|
124
|
+
function clearRangeMetadata() {
|
|
125
|
+
rangeMetadataMap.clear();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build the SQL for a range literal
|
|
129
|
+
*
|
|
130
|
+
* @param lower - Lower bound of the range
|
|
131
|
+
* @param upper - Upper bound of the range
|
|
132
|
+
* @param options - Options for the range
|
|
133
|
+
* @returns SQL for the range literal
|
|
134
|
+
*/
|
|
135
|
+
function buildRangeLiteral(lower, upper, options) {
|
|
136
|
+
const lowerBracket = options?.inclusiveLower !== false ? '[' : '(';
|
|
137
|
+
const upperBracket = options?.inclusiveUpper !== false ? ']' : ')';
|
|
138
|
+
const formatBound = (bound) => {
|
|
139
|
+
if (bound === null || bound === undefined) {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
if (typeof bound === 'string') {
|
|
143
|
+
return `'${bound}'`;
|
|
144
|
+
}
|
|
145
|
+
if (bound instanceof Date) {
|
|
146
|
+
return `'${bound.toISOString()}'`;
|
|
147
|
+
}
|
|
148
|
+
return String(bound);
|
|
149
|
+
};
|
|
150
|
+
return `${lowerBracket}${formatBound(lower)}, ${formatBound(upper)}${upperBracket}::${'tsrange'}`;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get the SQL operator for range containment
|
|
154
|
+
*
|
|
155
|
+
* @returns SQL for the @> operator
|
|
156
|
+
*/
|
|
157
|
+
function getRangeContainedBySQL() {
|
|
158
|
+
return '<@';
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get the SQL operator for range overlap
|
|
162
|
+
*
|
|
163
|
+
* @returns SQL for the && operator
|
|
164
|
+
*/
|
|
165
|
+
function getRangeOverlapSQL() {
|
|
166
|
+
return '&&';
|
|
167
|
+
}
|