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,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dialect-agnostic fallback implementation of `Dialect.queryStream()`.
|
|
4
|
+
*
|
|
5
|
+
* Dialects whose driver exposes a real server-side cursor (postgres, mysql,
|
|
6
|
+
* cockroachdb) implement `queryStream` natively for true constant-memory
|
|
7
|
+
* streaming. Every other dialect uses `createPaginatedQueryStream()` here:
|
|
8
|
+
* it pages through the query via repeated `LIMIT`/`OFFSET`-equivalent
|
|
9
|
+
* queries (one dialect-appropriate page-clause per round trip, built with
|
|
10
|
+
* the dialect's own `buildLimitOffset()` by default), instead of loading the
|
|
11
|
+
* whole result set into memory at once. It's not as fast as a native cursor
|
|
12
|
+
* for very large result sets — each page is a fresh query — but it's
|
|
13
|
+
* correct on every dialect that can run a `SELECT * FROM (<sql>) AS alias`
|
|
14
|
+
* wrapped subquery with a trailing pagination clause, which is effectively
|
|
15
|
+
* all of them.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.buildPaginatedPageQuery = buildPaginatedPageQuery;
|
|
19
|
+
exports.createPaginatedQueryStream = createPaginatedQueryStream;
|
|
20
|
+
exports.createOracleQueryStream = createOracleQueryStream;
|
|
21
|
+
const stream_1 = require("stream");
|
|
22
|
+
/** Strips a single trailing `;` (and surrounding whitespace) so the query is safe to wrap as a subquery. */
|
|
23
|
+
function stripTrailingSemicolon(sql) {
|
|
24
|
+
return sql.trim().replace(/;\s*$/, '');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build the SQL for one page of a paginated stream. Exposed separately from
|
|
28
|
+
* `createPaginatedQueryStream()` so dialects with an unusual pagination
|
|
29
|
+
* syntax (see `createOracleQueryStream()`) can supply their own page-builder
|
|
30
|
+
* while still reusing the shared stream/backpressure/model-mapping logic.
|
|
31
|
+
*/
|
|
32
|
+
function buildPaginatedPageQuery(dialect, sql, limit, offset) {
|
|
33
|
+
const inner = stripTrailingSemicolon(sql);
|
|
34
|
+
const pageClause = dialect.buildLimitOffset(limit, offset);
|
|
35
|
+
return `SELECT * FROM (${inner}) AS __prorm_stream_page ${pageClause}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Core pull-based streaming loop shared by every fallback implementation.
|
|
39
|
+
* `fetchPage(limit, offset)` is called once per page, only after the
|
|
40
|
+
* previous page has been fully drained by the consumer (real backpressure,
|
|
41
|
+
* not read-ahead buffering) — it must return the raw rows for that page.
|
|
42
|
+
*/
|
|
43
|
+
function createPageDrivenStream(fetchPage, options) {
|
|
44
|
+
const batchSize = options?.batchSize || 1000;
|
|
45
|
+
const highWaterMark = options?.highWaterMark || 1000;
|
|
46
|
+
let offset = 0;
|
|
47
|
+
let buffer = [];
|
|
48
|
+
let bufferIndex = 0;
|
|
49
|
+
let exhausted = false;
|
|
50
|
+
let fetching = false;
|
|
51
|
+
const stream = new stream_1.Readable({
|
|
52
|
+
objectMode: true,
|
|
53
|
+
highWaterMark,
|
|
54
|
+
read() {
|
|
55
|
+
pump();
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
function pushRow(row) {
|
|
59
|
+
if (options?.mapToModel && options?.model) {
|
|
60
|
+
const instance = new options.model();
|
|
61
|
+
Object.assign(instance, row);
|
|
62
|
+
return stream.push(instance);
|
|
63
|
+
}
|
|
64
|
+
return stream.push(row);
|
|
65
|
+
}
|
|
66
|
+
function pump() {
|
|
67
|
+
if (fetching)
|
|
68
|
+
return;
|
|
69
|
+
// Drain whatever is already buffered first.
|
|
70
|
+
while (bufferIndex < buffer.length) {
|
|
71
|
+
const canContinue = pushRow(buffer[bufferIndex]);
|
|
72
|
+
bufferIndex++;
|
|
73
|
+
if (!canContinue)
|
|
74
|
+
return; // consumer applied backpressure; resume on next read()
|
|
75
|
+
}
|
|
76
|
+
if (exhausted) {
|
|
77
|
+
stream.push(null);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
fetching = true;
|
|
81
|
+
fetchPage(batchSize, offset)
|
|
82
|
+
.then((rows) => {
|
|
83
|
+
fetching = false;
|
|
84
|
+
buffer = rows;
|
|
85
|
+
bufferIndex = 0;
|
|
86
|
+
offset += rows.length;
|
|
87
|
+
if (rows.length < batchSize) {
|
|
88
|
+
exhausted = true;
|
|
89
|
+
}
|
|
90
|
+
if (rows.length === 0) {
|
|
91
|
+
stream.push(null);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
pump();
|
|
95
|
+
})
|
|
96
|
+
.catch((error) => {
|
|
97
|
+
fetching = false;
|
|
98
|
+
stream.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return stream;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Generic LIMIT/OFFSET-paginated `queryStream` fallback. Suitable for any
|
|
105
|
+
* dialect whose `buildLimitOffset()` returns a self-sufficient trailing
|
|
106
|
+
* clause (sqlite, mariadb, redshift, snowflake, clickhouse, duckdb, hana,
|
|
107
|
+
* spanner, mssql, db2 all qualify — mssql's own `buildLimitOffset` already
|
|
108
|
+
* injects a no-op `ORDER BY` when none is supplied, so it works unmodified
|
|
109
|
+
* here).
|
|
110
|
+
*/
|
|
111
|
+
function createPaginatedQueryStream(dialect, sql, options) {
|
|
112
|
+
if (!sql || !sql.trim()) {
|
|
113
|
+
const stream = new stream_1.Readable({ objectMode: true, read() { } });
|
|
114
|
+
process.nextTick(() => stream.destroy(new Error('queryStream: sql must be a non-empty string')));
|
|
115
|
+
return stream;
|
|
116
|
+
}
|
|
117
|
+
return createPageDrivenStream(async (limit, offset) => {
|
|
118
|
+
const pageSql = buildPaginatedPageQuery(dialect, sql, limit, offset);
|
|
119
|
+
const result = await dialect.query(pageSql, options?.transaction ? { transaction: options.transaction } : undefined);
|
|
120
|
+
return result.rows || [];
|
|
121
|
+
}, options);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Oracle-specific streaming. Oracle's `buildLimitOffset()` returns a bare
|
|
125
|
+
* `WHERE ROWNUM <= n` fragment that only limits the *first* n rows of the
|
|
126
|
+
* result set — it doesn't skip the first `offset` rows, so wrapping it the
|
|
127
|
+
* same way as `createPaginatedQueryStream()` would return the same leading
|
|
128
|
+
* rows on every page instead of paging forward. Rather than depend on that,
|
|
129
|
+
* this builds the standard "windowed ROWNUM" pagination idiom directly,
|
|
130
|
+
* which is correct for offset+limit on any Oracle version (no dependency on
|
|
131
|
+
* 12c's OFFSET/FETCH syntax):
|
|
132
|
+
*
|
|
133
|
+
* SELECT * FROM (
|
|
134
|
+
* SELECT a.*, ROWNUM rnum FROM (<sql>) a WHERE ROWNUM <= :offset + :limit
|
|
135
|
+
* ) WHERE rnum > :offset
|
|
136
|
+
*/
|
|
137
|
+
function createOracleQueryStream(dialect, sql, options) {
|
|
138
|
+
if (!sql || !sql.trim()) {
|
|
139
|
+
const stream = new stream_1.Readable({ objectMode: true, read() { } });
|
|
140
|
+
process.nextTick(() => stream.destroy(new Error('queryStream: sql must be a non-empty string')));
|
|
141
|
+
return stream;
|
|
142
|
+
}
|
|
143
|
+
const inner = stripTrailingSemicolon(sql);
|
|
144
|
+
return createPageDrivenStream(async (limit, offset) => {
|
|
145
|
+
const upperBound = offset + limit;
|
|
146
|
+
const pageSql = `SELECT * FROM (SELECT __prorm_inner.*, ROWNUM __prorm_rnum FROM (${inner}) __prorm_inner ` +
|
|
147
|
+
`WHERE ROWNUM <= ${upperBound}) WHERE __prorm_rnum > ${offset}`;
|
|
148
|
+
const result = await dialect.query(pageSql, options?.transaction ? { transaction: options.transaction } : undefined);
|
|
149
|
+
// Strip the synthetic __prorm_rnum column so callers see only real columns.
|
|
150
|
+
return (result.rows || []).map(({ __prorm_rnum, ...row }) => row);
|
|
151
|
+
}, options);
|
|
152
|
+
}
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* QuestDB dialect implementation for the TypeScript ORM
|
|
4
|
+
*
|
|
5
|
+
* QuestDB is a high-performance, column-oriented time-series database that
|
|
6
|
+
* speaks the PostgreSQL wire protocol, so this dialect extends
|
|
7
|
+
* `PostgresDialect` and reuses the exact same `pg` driver. Everything is
|
|
8
|
+
* inherited from Postgres except the handful of areas where QuestDB's SQL
|
|
9
|
+
* dialect genuinely diverges. The notable differences this subclass models:
|
|
10
|
+
*
|
|
11
|
+
* - Default SQL port is 8812 (QuestDB's `pgwire` port), not Postgres's 5432.
|
|
12
|
+
*
|
|
13
|
+
* - Designated timestamp + partitioning. Time-series tables in QuestDB elect
|
|
14
|
+
* one `TIMESTAMP` column as the "designated timestamp" and are physically
|
|
15
|
+
* partitioned by a time unit:
|
|
16
|
+
* `CREATE TABLE t (...) TIMESTAMP(ts) PARTITION BY DAY`
|
|
17
|
+
* Supported partition units are `NONE | YEAR | MONTH | DAY | HOUR` (WEEK is
|
|
18
|
+
* also accepted by QuestDB but rarely used). These are supplied via the
|
|
19
|
+
* table options (`timestamp` + `partitionBy`).
|
|
20
|
+
*
|
|
21
|
+
* - `SYMBOL` type for low-cardinality repeated strings (e.g. ticker symbols,
|
|
22
|
+
* device ids). QuestDB stores SYMBOLs as an interned dictionary of ints,
|
|
23
|
+
* which is far more compact/faster than STRING for repeated values. Columns
|
|
24
|
+
* can opt into `SYMBOL CAPACITY n` (dictionary size hint) and `CACHE`
|
|
25
|
+
* (keep the dictionary in memory).
|
|
26
|
+
*
|
|
27
|
+
* - WAL tables. Partitioned tables can be created with a write-ahead log
|
|
28
|
+
* (`WAL`, the default in modern QuestDB) or without it (`BYPASS WAL`).
|
|
29
|
+
*
|
|
30
|
+
* - No traditional constraints. QuestDB has NO PRIMARY KEY, NO FOREIGN KEY,
|
|
31
|
+
* NO UNIQUE, NO NOT NULL and NO DEFAULT clauses in `CREATE TABLE` — those
|
|
32
|
+
* concepts don't exist in the engine, so they are deliberately omitted from
|
|
33
|
+
* the generated DDL. Row de-duplication is instead expressed with a
|
|
34
|
+
* `DEDUP UPSERT KEYS(...)` clause (WAL tables only), which upserts rows that
|
|
35
|
+
* share the given key columns (the designated timestamp must be one of
|
|
36
|
+
* them).
|
|
37
|
+
*
|
|
38
|
+
* - Type mapping. QuestDB's type names differ from Postgres's:
|
|
39
|
+
* INTEGER -> INT, BIGINT -> LONG, FLOAT -> FLOAT, DOUBLE -> DOUBLE,
|
|
40
|
+
* BOOLEAN -> BOOLEAN, STRING/TEXT/CHAR -> STRING, SYMBOL -> SYMBOL,
|
|
41
|
+
* DATE/DATETIME/TIME -> TIMESTAMP, BLOB -> BINARY, UUID -> UUID.
|
|
42
|
+
*
|
|
43
|
+
* The core DDL builder here (`buildCreateTableSQL`) is a pure, synchronous
|
|
44
|
+
* function that returns the SQL string, so it can be unit-tested without a
|
|
45
|
+
* live connection; `createTable()` simply runs it through the inherited
|
|
46
|
+
* `query()`.
|
|
47
|
+
*
|
|
48
|
+
* @see https://questdb.io/docs/
|
|
49
|
+
*/
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.QuestDBDialect = void 0;
|
|
52
|
+
exports.createQuestDBDialect = createQuestDBDialect;
|
|
53
|
+
const index_1 = require("../postgres/index");
|
|
54
|
+
const prorm_1 = require("../../prorm");
|
|
55
|
+
/**
|
|
56
|
+
* QuestDB dialect class. Extends the PostgreSQL dialect (QuestDB is
|
|
57
|
+
* PG-wire-compatible) and overrides only the CREATE TABLE DDL and the type
|
|
58
|
+
* mapping to reflect QuestDB's time-series-specific SQL.
|
|
59
|
+
*/
|
|
60
|
+
class QuestDBDialect extends index_1.PostgresDialect {
|
|
61
|
+
constructor(config = {}) {
|
|
62
|
+
// QuestDB's PostgreSQL-wire (pgwire) port defaults to 8812.
|
|
63
|
+
super({ port: 8812, ...config });
|
|
64
|
+
this.name = 'questdb';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build the column fragment for a single column, honouring QuestDB's
|
|
68
|
+
* `SYMBOL [CAPACITY n] [CACHE]` syntax and deliberately omitting the
|
|
69
|
+
* PRIMARY KEY / UNIQUE / FOREIGN KEY / NOT NULL / DEFAULT clauses that
|
|
70
|
+
* QuestDB does not support in CREATE TABLE.
|
|
71
|
+
*/
|
|
72
|
+
buildQuestColumnSql(columnName, definition) {
|
|
73
|
+
const dt = definition.type;
|
|
74
|
+
const isSymbol = definition.symbol === true ||
|
|
75
|
+
(typeof dt === 'object' && dt !== null && dt.key === 'SYMBOL') ||
|
|
76
|
+
(typeof definition.type === 'string' && definition.type.toUpperCase() === 'SYMBOL');
|
|
77
|
+
let typeSql;
|
|
78
|
+
if (isSymbol) {
|
|
79
|
+
typeSql = 'SYMBOL';
|
|
80
|
+
if (definition.symbolCapacity) {
|
|
81
|
+
typeSql += ` CAPACITY ${definition.symbolCapacity}`;
|
|
82
|
+
}
|
|
83
|
+
if (definition.symbolCache) {
|
|
84
|
+
typeSql += ' CACHE';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
typeSql = this.getDataTypeSql(definition.type);
|
|
89
|
+
}
|
|
90
|
+
// Note: PRIMARY KEY / UNIQUE / FOREIGN KEY / NOT NULL / DEFAULT are
|
|
91
|
+
// intentionally NOT emitted — QuestDB's CREATE TABLE grammar has no such
|
|
92
|
+
// column constraints.
|
|
93
|
+
return `${this.escapeId(columnName)} ${typeSql}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Pure, synchronous builder for a QuestDB `CREATE TABLE` statement. Returns
|
|
97
|
+
* the SQL string so it can be unit-tested without a database connection.
|
|
98
|
+
*
|
|
99
|
+
* Emits, in order:
|
|
100
|
+
* CREATE TABLE [IF NOT EXISTS] name (cols...)
|
|
101
|
+
* [TIMESTAMP(designated) PARTITION BY UNIT [WAL|BYPASS WAL]]
|
|
102
|
+
* [DEDUP UPSERT KEYS(...)]
|
|
103
|
+
*/
|
|
104
|
+
buildCreateTableSQL(tableName, columns, options) {
|
|
105
|
+
const columnDefs = [];
|
|
106
|
+
for (const [columnName, definition] of Object.entries(columns)) {
|
|
107
|
+
columnDefs.push(this.buildQuestColumnSql(columnName, definition));
|
|
108
|
+
}
|
|
109
|
+
let sql = 'CREATE TABLE';
|
|
110
|
+
if (options?.ifNotExists) {
|
|
111
|
+
sql += ' IF NOT EXISTS';
|
|
112
|
+
}
|
|
113
|
+
sql += ` ${this.escapeId(tableName)} (${columnDefs.join(', ')})`;
|
|
114
|
+
// Designated timestamp + time partitioning. QuestDB only partitions tables
|
|
115
|
+
// that declare a designated timestamp, so PARTITION BY is emitted inside
|
|
116
|
+
// the timestamp branch.
|
|
117
|
+
if (options?.timestamp) {
|
|
118
|
+
sql += ` TIMESTAMP(${this.escapeId(options.timestamp)})`;
|
|
119
|
+
const unit = options.partitionBy ?? 'NONE';
|
|
120
|
+
sql += ` PARTITION BY ${unit}`;
|
|
121
|
+
// WAL / BYPASS WAL applies to partitioned tables.
|
|
122
|
+
if (options.wal === true) {
|
|
123
|
+
sql += ' WAL';
|
|
124
|
+
}
|
|
125
|
+
else if (options.wal === false) {
|
|
126
|
+
sql += ' BYPASS WAL';
|
|
127
|
+
}
|
|
128
|
+
// DEDUP requires a designated timestamp (and a WAL table).
|
|
129
|
+
if (options.dedupUpsertKeys && options.dedupUpsertKeys.length > 0) {
|
|
130
|
+
const keys = options.dedupUpsertKeys.map((k) => this.escapeId(k)).join(', ');
|
|
131
|
+
sql += ` DEDUP UPSERT KEYS(${keys})`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return sql;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create a new table using QuestDB's CREATE TABLE grammar. Delegates SQL
|
|
138
|
+
* construction to the synchronous {@link buildCreateTableSQL} and runs it
|
|
139
|
+
* through the inherited `query()`.
|
|
140
|
+
*/
|
|
141
|
+
async createTable(tableName, columns, options) {
|
|
142
|
+
const sql = this.buildCreateTableSQL(tableName, columns, options);
|
|
143
|
+
await this.query(sql);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Map an ORM data type to QuestDB's type name. QuestDB's type vocabulary
|
|
147
|
+
* differs from PostgreSQL's, so this overrides the inherited mapping.
|
|
148
|
+
* @see https://questdb.io/docs/reference/sql/datatypes/
|
|
149
|
+
*/
|
|
150
|
+
getDataTypeSql(dataType) {
|
|
151
|
+
// String shorthand types (e.g. 'BIGINT', 'DATETIME') — normalize the
|
|
152
|
+
// common cross-dialect names to QuestDB equivalents.
|
|
153
|
+
if (typeof dataType === 'string') {
|
|
154
|
+
const up = dataType.toUpperCase();
|
|
155
|
+
if (/^DATETIME(\(\d+\))?$/.test(up) || up === 'TIMESTAMP' || up === 'DATE' || up === 'TIME') {
|
|
156
|
+
return 'TIMESTAMP';
|
|
157
|
+
}
|
|
158
|
+
if (up === 'BIGINT')
|
|
159
|
+
return 'LONG';
|
|
160
|
+
if (up === 'INTEGER' || up === 'INT')
|
|
161
|
+
return 'INT';
|
|
162
|
+
if (up === 'DOUBLE' || up === 'DOUBLE PRECISION' || up === 'DECIMAL' || up === 'NUMERIC') {
|
|
163
|
+
return 'DOUBLE';
|
|
164
|
+
}
|
|
165
|
+
if (up === 'REAL')
|
|
166
|
+
return 'FLOAT';
|
|
167
|
+
if (up === 'TEXT' || up === 'VARCHAR' || up === 'CHAR' || up === 'STRING')
|
|
168
|
+
return 'STRING';
|
|
169
|
+
if (up === 'SYMBOL')
|
|
170
|
+
return 'SYMBOL';
|
|
171
|
+
if (up === 'BOOL' || up === 'BOOLEAN')
|
|
172
|
+
return 'BOOLEAN';
|
|
173
|
+
return dataType;
|
|
174
|
+
}
|
|
175
|
+
if (!dataType || typeof dataType !== 'object') {
|
|
176
|
+
return 'STRING';
|
|
177
|
+
}
|
|
178
|
+
const dt = dataType;
|
|
179
|
+
switch (dt.key) {
|
|
180
|
+
case 'INTEGER':
|
|
181
|
+
return 'INT';
|
|
182
|
+
case 'BIGINT':
|
|
183
|
+
return 'LONG';
|
|
184
|
+
case 'FLOAT':
|
|
185
|
+
return 'FLOAT';
|
|
186
|
+
case 'DOUBLE':
|
|
187
|
+
case 'DECIMAL':
|
|
188
|
+
return 'DOUBLE';
|
|
189
|
+
case 'BOOLEAN':
|
|
190
|
+
return 'BOOLEAN';
|
|
191
|
+
case 'STRING':
|
|
192
|
+
case 'TEXT':
|
|
193
|
+
case 'CHAR':
|
|
194
|
+
return 'STRING';
|
|
195
|
+
case 'SYMBOL':
|
|
196
|
+
return 'SYMBOL';
|
|
197
|
+
case 'DATE':
|
|
198
|
+
case 'DATEONLY':
|
|
199
|
+
case 'TIME':
|
|
200
|
+
return 'TIMESTAMP';
|
|
201
|
+
case 'UUID':
|
|
202
|
+
return 'UUID';
|
|
203
|
+
case 'BLOB':
|
|
204
|
+
return 'BINARY';
|
|
205
|
+
case 'JSON':
|
|
206
|
+
case 'JSONB':
|
|
207
|
+
// QuestDB has no native JSON type; store as STRING.
|
|
208
|
+
return 'STRING';
|
|
209
|
+
default:
|
|
210
|
+
// Fall back to the PostgreSQL mapping for anything QuestDB shares.
|
|
211
|
+
return super.getDataTypeSql(dataType);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ===========================================================================
|
|
215
|
+
// Pagination
|
|
216
|
+
// ===========================================================================
|
|
217
|
+
/**
|
|
218
|
+
* Build a QuestDB `LIMIT` clause. QuestDB does NOT support the SQL-standard
|
|
219
|
+
* `OFFSET` keyword; instead it uses:
|
|
220
|
+
* - `LIMIT n` — first `n` rows (or last `n` when negative).
|
|
221
|
+
* - `LIMIT lo, hi` — rows in the half-open range `(lo, hi]`, which is how
|
|
222
|
+
* an offset is expressed. To skip `offset` rows and
|
|
223
|
+
* return `limit` of them we emit `LIMIT offset, offset+limit`.
|
|
224
|
+
*
|
|
225
|
+
* @see https://questdb.io/docs/reference/sql/limit/
|
|
226
|
+
*/
|
|
227
|
+
buildLimitOffset(limit, offset) {
|
|
228
|
+
const hasLimit = limit !== undefined && limit !== null;
|
|
229
|
+
const hasOffset = offset !== undefined && offset !== null;
|
|
230
|
+
if (hasLimit && hasOffset) {
|
|
231
|
+
const lo = Number(offset);
|
|
232
|
+
const hi = lo + Number(limit);
|
|
233
|
+
return ` LIMIT ${lo}, ${hi}`;
|
|
234
|
+
}
|
|
235
|
+
if (hasLimit) {
|
|
236
|
+
return ` LIMIT ${Number(limit)}`;
|
|
237
|
+
}
|
|
238
|
+
// Offset-only has no direct QuestDB form (an upper bound is required for
|
|
239
|
+
// `LIMIT lo, hi`), so nothing is emitted. Callers needing a lower bound
|
|
240
|
+
// without an explicit page size should pass an explicit `limit`.
|
|
241
|
+
return '';
|
|
242
|
+
}
|
|
243
|
+
// ===========================================================================
|
|
244
|
+
// DML — INSERT / UPSERT / UPDATE / DELETE
|
|
245
|
+
// ===========================================================================
|
|
246
|
+
/**
|
|
247
|
+
* Build a QuestDB `INSERT`. QuestDB has **no** `ON CONFLICT` clause (row
|
|
248
|
+
* de-duplication is configured on the table via `DEDUP UPSERT KEYS(...)` at
|
|
249
|
+
* `CREATE TABLE` time) and **no** `RETURNING` clause, so both are omitted —
|
|
250
|
+
* any `upsert`/`conflictFields`/`returning` options are ignored here.
|
|
251
|
+
*/
|
|
252
|
+
buildInsertQuery(tableName, values, _options) {
|
|
253
|
+
const columns = Object.keys(values);
|
|
254
|
+
const processedValues = [];
|
|
255
|
+
const placeholders = [];
|
|
256
|
+
let paramIndex = 1;
|
|
257
|
+
for (const value of Object.values(values)) {
|
|
258
|
+
if (value instanceof prorm_1.Literal) {
|
|
259
|
+
placeholders.push(value.val);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
placeholders.push(`$${paramIndex}`);
|
|
263
|
+
processedValues.push(value);
|
|
264
|
+
paramIndex++;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const sql = `INSERT INTO ${this.escapeId(tableName)} ` +
|
|
268
|
+
`(${columns.map((c) => this.escapeId(c)).join(', ')}) ` +
|
|
269
|
+
`VALUES (${placeholders.join(', ')})`;
|
|
270
|
+
return { sql, values: processedValues };
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* QuestDB has no `INSERT ... ON CONFLICT DO UPDATE`. The closest concept is a
|
|
274
|
+
* table-level `DEDUP UPSERT KEYS(...)` declaration, after which a plain
|
|
275
|
+
* `INSERT` transparently upserts rows sharing the dedup keys. This therefore
|
|
276
|
+
* emits a bare `INSERT` (no `ON CONFLICT`, no `RETURNING`); `conflictFields`
|
|
277
|
+
* and `updateOnDuplicate` are ignored because QuestDB resolves conflicts from
|
|
278
|
+
* the table's DEDUP configuration, not the statement.
|
|
279
|
+
*/
|
|
280
|
+
buildUpsertQuery(tableName, values, _options) {
|
|
281
|
+
return this.buildInsertQuery(tableName, values);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Build a QuestDB `UPDATE`. QuestDB supports `UPDATE t SET ... WHERE ...` but
|
|
285
|
+
* does NOT support `LIMIT` on updates nor a `RETURNING` clause, so those
|
|
286
|
+
* options are omitted.
|
|
287
|
+
* @see https://questdb.io/docs/reference/sql/update/
|
|
288
|
+
*/
|
|
289
|
+
buildUpdateQuery(tableName, values, where, _options) {
|
|
290
|
+
const setClauses = [];
|
|
291
|
+
const queryValues = [];
|
|
292
|
+
let paramIndex = 1;
|
|
293
|
+
for (const [key, value] of Object.entries(values)) {
|
|
294
|
+
if (value instanceof prorm_1.Literal) {
|
|
295
|
+
setClauses.push(`${this.escapeId(key)} = ${value.val}`);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
setClauses.push(`${this.escapeId(key)} = $${paramIndex}`);
|
|
299
|
+
queryValues.push(value);
|
|
300
|
+
paramIndex++;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const whereClause = this.buildWhereClause(where);
|
|
304
|
+
let sql = `UPDATE ${this.escapeId(tableName)} SET ${setClauses.join(', ')}`;
|
|
305
|
+
if (whereClause.sql) {
|
|
306
|
+
sql += ` WHERE ${whereClause.sql}`;
|
|
307
|
+
}
|
|
308
|
+
return { sql, values: [...queryValues, ...whereClause.values] };
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Build a QuestDB "delete". IMPORTANT: QuestDB does **not** support
|
|
312
|
+
* row-level `DELETE`. Bulk removal is done by dropping whole time partitions
|
|
313
|
+
* (`ALTER TABLE t DROP PARTITION ...`) or emptying the table
|
|
314
|
+
* (`TRUNCATE TABLE t`). Accordingly:
|
|
315
|
+
* - `options.truncate` produces a valid `TRUNCATE TABLE t` statement.
|
|
316
|
+
* - Any other call throws, because emitting a `DELETE ... WHERE ...` that
|
|
317
|
+
* the server will reject would be misleading. Callers wanting to remove
|
|
318
|
+
* a time range should drop the relevant partition(s) via raw SQL / the
|
|
319
|
+
* partition APIs instead.
|
|
320
|
+
* @see https://questdb.io/docs/reference/sql/alter-table-drop-partition/
|
|
321
|
+
*/
|
|
322
|
+
buildDeleteQuery(tableName, _where, options) {
|
|
323
|
+
if (options?.truncate) {
|
|
324
|
+
return { sql: `TRUNCATE TABLE ${this.escapeId(tableName)}`, values: [] };
|
|
325
|
+
}
|
|
326
|
+
throw new Error('QuestDB does not support row-level DELETE. Remove data by dropping ' +
|
|
327
|
+
'time partitions (ALTER TABLE ... DROP PARTITION ...) or truncating ' +
|
|
328
|
+
'the table (pass { truncate: true }).');
|
|
329
|
+
}
|
|
330
|
+
// ===========================================================================
|
|
331
|
+
// DDL — columns, indexes, rename, unsupported constraints
|
|
332
|
+
// ===========================================================================
|
|
333
|
+
/**
|
|
334
|
+
* Pure builder for `ALTER TABLE t ADD COLUMN name type` (testable without a
|
|
335
|
+
* connection). QuestDB supports adding a column but NOT the per-column
|
|
336
|
+
* constraints (NOT NULL / DEFAULT / PRIMARY KEY / UNIQUE / REFERENCES) that
|
|
337
|
+
* the Postgres builder would emit, so this reuses the QuestDB column builder.
|
|
338
|
+
*/
|
|
339
|
+
buildAddColumnSQL(tableName, columnName, definition) {
|
|
340
|
+
return `ALTER TABLE ${this.escapeId(tableName)} ADD COLUMN ${this.buildQuestColumnSql(columnName, definition)}`;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Add a column. Overridden so QuestDB's constraint-free `ADD COLUMN` grammar
|
|
344
|
+
* (and SYMBOL support) is used rather than the inherited Postgres one.
|
|
345
|
+
*/
|
|
346
|
+
async addColumn(tableName, columnName, definition) {
|
|
347
|
+
await this.query(this.buildAddColumnSQL(tableName, columnName, definition));
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Pure builder for a QuestDB SYMBOL-column index. QuestDB has no
|
|
351
|
+
* PostgreSQL-style `CREATE INDEX`; indexes exist only on `SYMBOL` columns and
|
|
352
|
+
* are added via `ALTER TABLE t ALTER COLUMN col ADD INDEX [CAPACITY n]`.
|
|
353
|
+
* @see https://questdb.io/docs/reference/sql/alter-table-alter-column-add-index/
|
|
354
|
+
*/
|
|
355
|
+
buildAddIndexSQL(tableName, column, capacity) {
|
|
356
|
+
let sql = `ALTER TABLE ${this.escapeId(tableName)} ALTER COLUMN ${this.escapeId(column)} ADD INDEX`;
|
|
357
|
+
if (capacity) {
|
|
358
|
+
sql += ` CAPACITY ${capacity}`;
|
|
359
|
+
}
|
|
360
|
+
return sql;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Pure builder for dropping a QuestDB SYMBOL index:
|
|
364
|
+
* `ALTER TABLE t ALTER COLUMN col DROP INDEX`.
|
|
365
|
+
*/
|
|
366
|
+
buildDropIndexSQL(tableName, column) {
|
|
367
|
+
return `ALTER TABLE ${this.escapeId(tableName)} ALTER COLUMN ${this.escapeId(column)} DROP INDEX`;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Add an index. In QuestDB an "index" is a SYMBOL-column index, so each field
|
|
371
|
+
* is indexed with its own `ALTER COLUMN ... ADD INDEX`. The `indexName` and
|
|
372
|
+
* PG-only options (unique/using/expression/where/include) have no meaning and
|
|
373
|
+
* are ignored.
|
|
374
|
+
*/
|
|
375
|
+
async addIndex(tableName, _indexName, fields = [], options) {
|
|
376
|
+
for (const field of fields) {
|
|
377
|
+
await this.query(this.buildAddIndexSQL(tableName, field, options?.capacity));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Create an index (SYMBOL-column index) from an {@link IndexDefinition}.
|
|
382
|
+
*/
|
|
383
|
+
async createIndex(tableName, indexDef) {
|
|
384
|
+
for (const field of indexDef.fields || []) {
|
|
385
|
+
await this.query(this.buildAddIndexSQL(tableName, field, indexDef.capacity));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Remove an index. QuestDB drops the index off the SYMBOL column, so the
|
|
390
|
+
* `indexName` argument is interpreted as the column name.
|
|
391
|
+
*/
|
|
392
|
+
async removeIndex(tableName, columnName) {
|
|
393
|
+
await this.query(this.buildDropIndexSQL(tableName, columnName));
|
|
394
|
+
}
|
|
395
|
+
async dropIndex(tableName, columnName, _options) {
|
|
396
|
+
await this.query(this.buildDropIndexSQL(tableName, columnName));
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Pure builder for `RENAME TABLE old TO new`. QuestDB renames tables with a
|
|
400
|
+
* dedicated `RENAME TABLE` statement, NOT `ALTER TABLE ... RENAME TO`.
|
|
401
|
+
* @see https://questdb.io/docs/reference/sql/rename/
|
|
402
|
+
*/
|
|
403
|
+
buildRenameTableSQL(oldName, newName) {
|
|
404
|
+
return `RENAME TABLE ${this.escapeId(oldName)} TO ${this.escapeId(newName)}`;
|
|
405
|
+
}
|
|
406
|
+
async renameTable(oldName, newName) {
|
|
407
|
+
await this.query(this.buildRenameTableSQL(oldName, newName));
|
|
408
|
+
}
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
410
|
+
// Unsupported relational constraints. QuestDB has no PRIMARY KEY / FOREIGN
|
|
411
|
+
// KEY / UNIQUE / CHECK constraints, so these fail loudly rather than emitting
|
|
412
|
+
// SQL the engine cannot parse.
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
async addForeignKey() {
|
|
415
|
+
throw new Error('QuestDB does not support FOREIGN KEY constraints.');
|
|
416
|
+
}
|
|
417
|
+
async addConstraint() {
|
|
418
|
+
throw new Error('QuestDB does not support table constraints (PRIMARY KEY / FOREIGN KEY / ' +
|
|
419
|
+
'UNIQUE / CHECK). Use DEDUP UPSERT KEYS on CREATE TABLE for row de-duplication.');
|
|
420
|
+
}
|
|
421
|
+
async createConstraint(_tableName, _constraintDef) {
|
|
422
|
+
throw new Error('QuestDB does not support table constraints (PRIMARY KEY / FOREIGN KEY / ' +
|
|
423
|
+
'UNIQUE / CHECK). Use DEDUP UPSERT KEYS on CREATE TABLE for row de-duplication.');
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
exports.QuestDBDialect = QuestDBDialect;
|
|
427
|
+
/**
|
|
428
|
+
* Create a new QuestDB dialect instance.
|
|
429
|
+
*/
|
|
430
|
+
function createQuestDBDialect(options) {
|
|
431
|
+
return new QuestDBDialect(options || {});
|
|
432
|
+
}
|
|
433
|
+
exports.default = QuestDBDialect;
|