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,517 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Databricks SQL dialect implementation for the TypeScript ORM.
|
|
4
|
+
*
|
|
5
|
+
* Databricks SQL is built on Apache Spark SQL and the Delta Lake table format
|
|
6
|
+
* (a.k.a. the "lakehouse"). It uses backtick-quoted identifiers, exactly like
|
|
7
|
+
* MySQL/Spark SQL, so this dialect EXTENDS {@link MySQLDialect} purely to reuse
|
|
8
|
+
* the backtick quoting/escaping scaffolding and the general query-building
|
|
9
|
+
* skeleton, then overrides the pieces where Spark/Delta grammar genuinely
|
|
10
|
+
* differs from MySQL. A real deployment would connect over the Databricks SQL
|
|
11
|
+
* driver (Databricks SQL Warehouse / `@databricks/sql`) rather than mysql2 —
|
|
12
|
+
* the inherited connection code is not used against a real cluster.
|
|
13
|
+
*
|
|
14
|
+
* Notable differences from MySQL that this dialect overrides:
|
|
15
|
+
*
|
|
16
|
+
* - Default port is 443 (Databricks SQL Warehouses are reached over HTTPS),
|
|
17
|
+
* not 3306.
|
|
18
|
+
* - CREATE TABLE emits Delta/Spark grammar: `USING DELTA` (the table's data
|
|
19
|
+
* source/format, default DELTA; also PARQUET/CSV/JSON/etc.), `PARTITIONED BY
|
|
20
|
+
* (cols)`, `CLUSTERED BY (cols) INTO n BUCKETS`, `TBLPROPERTIES (...)`,
|
|
21
|
+
* `LOCATION 's3://...'` (external/unmanaged tables), and a trailing
|
|
22
|
+
* `COMMENT '...'`. There is no MySQL `ENGINE=`/`ROW_FORMAT=`/charset concept.
|
|
23
|
+
* - CTAS: {@link DatabricksDialect.buildCreateTableAsSelect} emits
|
|
24
|
+
* `CREATE TABLE ... USING DELTA AS SELECT ...`.
|
|
25
|
+
* - Upsert is expressed as a Delta Lake `MERGE INTO target USING source ON ...
|
|
26
|
+
* WHEN MATCHED THEN UPDATE SET ... WHEN NOT MATCHED THEN INSERT ...`
|
|
27
|
+
* statement (see {@link DatabricksDialect.buildUpsertQuery}) rather than
|
|
28
|
+
* MySQL's `INSERT ... ON DUPLICATE KEY UPDATE`.
|
|
29
|
+
* - Auto-increment columns use Delta's `GENERATED ALWAYS AS IDENTITY` identity
|
|
30
|
+
* columns instead of MySQL's `AUTO_INCREMENT`.
|
|
31
|
+
* - Type mapping uses Spark SQL types: `STRING` (there is no `VARCHAR`/`TEXT`),
|
|
32
|
+
* `BIGINT`, `INT`, `DOUBLE`, `FLOAT`, `BOOLEAN`, `TIMESTAMP`, `DATE`,
|
|
33
|
+
* `BINARY`, `DECIMAL(p,s)`, and the complex types `ARRAY<>`, `MAP<>`,
|
|
34
|
+
* `STRUCT<>` (see {@link DatabricksDialect.getDataTypeSql}).
|
|
35
|
+
* - PRIMARY KEY / FOREIGN KEY constraints in Databricks are INFORMATIONAL only
|
|
36
|
+
* (they are declared `NOT ENFORCED` and used by the optimizer for query
|
|
37
|
+
* planning); they do not enforce uniqueness or referential integrity. This
|
|
38
|
+
* dialect emits them inline as informational constraints and never relies on
|
|
39
|
+
* them for correctness.
|
|
40
|
+
*/
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.DatabricksDialect = void 0;
|
|
43
|
+
exports.createDatabricksDialect = createDatabricksDialect;
|
|
44
|
+
const index_1 = require("../mysql/index");
|
|
45
|
+
const prorm_1 = require("../../prorm");
|
|
46
|
+
/**
|
|
47
|
+
* Databricks SQL dialect. See the file header for the full list of
|
|
48
|
+
* Spark/Delta-specific overrides.
|
|
49
|
+
*/
|
|
50
|
+
class DatabricksDialect extends index_1.MySQLDialect {
|
|
51
|
+
constructor(config) {
|
|
52
|
+
// Databricks SQL Warehouses are reached over HTTPS on port 443.
|
|
53
|
+
super({ port: 443, ...config });
|
|
54
|
+
this.name = 'databricks';
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Map an ORM {@link DataType} to a Spark SQL / Delta type. The key
|
|
58
|
+
* difference from MySQL is that character types collapse to `STRING`
|
|
59
|
+
* (Spark SQL has no `VARCHAR(n)`/`TEXT`), and the complex types
|
|
60
|
+
* `ARRAY<>`/`MAP<>`/`STRUCT<>` are supported.
|
|
61
|
+
*/
|
|
62
|
+
getDataTypeSql(dataType) {
|
|
63
|
+
if (typeof dataType === 'string') {
|
|
64
|
+
const upper = dataType.trim().toUpperCase();
|
|
65
|
+
// Normalize MySQL-ish character types written as raw strings to STRING.
|
|
66
|
+
if (/^(VARCHAR|CHARACTER VARYING|NVARCHAR|CHAR|N?TEXT|TINYTEXT|MEDIUMTEXT|LONGTEXT|CLOB)\b/.test(upper)) {
|
|
67
|
+
return 'STRING';
|
|
68
|
+
}
|
|
69
|
+
return dataType;
|
|
70
|
+
}
|
|
71
|
+
if (!dataType || typeof dataType !== 'object') {
|
|
72
|
+
return 'STRING';
|
|
73
|
+
}
|
|
74
|
+
const dt = dataType;
|
|
75
|
+
switch (dt.key) {
|
|
76
|
+
case 'STRING':
|
|
77
|
+
case 'CHAR':
|
|
78
|
+
case 'TEXT':
|
|
79
|
+
case 'ENUM':
|
|
80
|
+
case 'SET':
|
|
81
|
+
case 'UUID':
|
|
82
|
+
case 'JSON':
|
|
83
|
+
case 'JSONB':
|
|
84
|
+
// Spark SQL has no VARCHAR/TEXT/ENUM/JSON column types; store as STRING.
|
|
85
|
+
return 'STRING';
|
|
86
|
+
case 'INTEGER':
|
|
87
|
+
if (dt.length === 1)
|
|
88
|
+
return 'TINYINT';
|
|
89
|
+
if (dt.length === 2)
|
|
90
|
+
return 'SMALLINT';
|
|
91
|
+
if (dt.length === 8)
|
|
92
|
+
return 'BIGINT';
|
|
93
|
+
return 'INT';
|
|
94
|
+
case 'BIGINT':
|
|
95
|
+
return 'BIGINT';
|
|
96
|
+
case 'FLOAT':
|
|
97
|
+
return 'FLOAT';
|
|
98
|
+
case 'DOUBLE':
|
|
99
|
+
return 'DOUBLE';
|
|
100
|
+
case 'DECIMAL':
|
|
101
|
+
return `DECIMAL(${dt.precision || 10},${dt.scale || 0})`;
|
|
102
|
+
case 'BOOLEAN':
|
|
103
|
+
return 'BOOLEAN';
|
|
104
|
+
case 'DATE':
|
|
105
|
+
// A full timestamp (date + time) maps to Spark TIMESTAMP.
|
|
106
|
+
return 'TIMESTAMP';
|
|
107
|
+
case 'DATEONLY':
|
|
108
|
+
return 'DATE';
|
|
109
|
+
case 'TIME':
|
|
110
|
+
// Spark SQL has no standalone TIME type; represent as STRING.
|
|
111
|
+
return 'STRING';
|
|
112
|
+
case 'BLOB':
|
|
113
|
+
return 'BINARY';
|
|
114
|
+
case 'ARRAY':
|
|
115
|
+
return `ARRAY<${this.getComplexElementType(dt.type)}>`;
|
|
116
|
+
case 'MAP':
|
|
117
|
+
return `MAP<${this.getComplexElementType(dt.keyType) || 'STRING'}, ${this.getComplexElementType(dt.valueType)}>`;
|
|
118
|
+
case 'STRUCT': {
|
|
119
|
+
const fields = dt.fields || {};
|
|
120
|
+
const fieldSql = Object.entries(fields)
|
|
121
|
+
.map(([name, type]) => `${name}: ${this.getDataTypeSql(type)}`)
|
|
122
|
+
.join(', ');
|
|
123
|
+
return `STRUCT<${fieldSql}>`;
|
|
124
|
+
}
|
|
125
|
+
default:
|
|
126
|
+
// Anything not explicitly Spark-mapped falls through to STRING rather
|
|
127
|
+
// than an unsupported MySQL type like VARCHAR(255).
|
|
128
|
+
return 'STRING';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolve the element/value type for a complex (ARRAY/MAP) type, defaulting
|
|
133
|
+
* to `STRING` when the element type is unspecified.
|
|
134
|
+
*/
|
|
135
|
+
getComplexElementType(type) {
|
|
136
|
+
if (type === undefined || type === null) {
|
|
137
|
+
return 'STRING';
|
|
138
|
+
}
|
|
139
|
+
return this.getDataTypeSql(type);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Build a single column definition for a Databricks CREATE TABLE. Emits
|
|
143
|
+
* Delta `GENERATED ALWAYS AS IDENTITY` for auto-increment columns and an
|
|
144
|
+
* informational (not-enforced) inline `PRIMARY KEY` where requested.
|
|
145
|
+
*/
|
|
146
|
+
buildColumnDefinitionSql(columnName, definition) {
|
|
147
|
+
let sql = `${this.escapeId(columnName)} ${this.getDataTypeSql(definition.type)}`;
|
|
148
|
+
if (definition.allowNull === false) {
|
|
149
|
+
sql += ' NOT NULL';
|
|
150
|
+
}
|
|
151
|
+
if (definition.autoIncrement) {
|
|
152
|
+
// Delta identity columns; there is no MySQL-style AUTO_INCREMENT.
|
|
153
|
+
sql += ' GENERATED ALWAYS AS IDENTITY';
|
|
154
|
+
}
|
|
155
|
+
if (definition.defaultValue !== undefined && !definition.autoIncrement) {
|
|
156
|
+
sql += ` DEFAULT ${this.escape(definition.defaultValue)}`;
|
|
157
|
+
}
|
|
158
|
+
if (definition.primaryKey) {
|
|
159
|
+
// Informational only in Databricks (declared NOT ENFORCED). Kept for
|
|
160
|
+
// optimizer hints; never relied on for uniqueness.
|
|
161
|
+
sql += ' PRIMARY KEY';
|
|
162
|
+
}
|
|
163
|
+
if (definition.comment) {
|
|
164
|
+
sql += ` COMMENT ${this.escape(definition.comment)}`;
|
|
165
|
+
}
|
|
166
|
+
return sql;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Build the `CREATE TABLE` statement using Delta/Spark grammar. Exposed
|
|
170
|
+
* synchronously (separate from the async {@link createTable}) so callers can
|
|
171
|
+
* inspect the generated SQL without a live connection.
|
|
172
|
+
*/
|
|
173
|
+
buildCreateTableSql(tableName, columns, options) {
|
|
174
|
+
const columnDefs = Object.entries(columns).map(([name, def]) => this.buildColumnDefinitionSql(name, def));
|
|
175
|
+
let sql = 'CREATE TABLE';
|
|
176
|
+
if (options?.ifNotExists) {
|
|
177
|
+
sql += ' IF NOT EXISTS';
|
|
178
|
+
}
|
|
179
|
+
sql += ` ${this.escapeId(tableName)} (${columnDefs.join(', ')})`;
|
|
180
|
+
// Data source / format. Delta is the default when unspecified.
|
|
181
|
+
const format = options?.using || 'DELTA';
|
|
182
|
+
sql += ` USING ${format}`;
|
|
183
|
+
if (options?.partitionedBy && options.partitionedBy.length > 0) {
|
|
184
|
+
sql += ` PARTITIONED BY (${options.partitionedBy.map((c) => this.escapeId(c)).join(', ')})`;
|
|
185
|
+
}
|
|
186
|
+
if (options?.clusteredBy && options.clusteredBy.columns.length > 0) {
|
|
187
|
+
const cols = options.clusteredBy.columns.map((c) => this.escapeId(c)).join(', ');
|
|
188
|
+
sql += ` CLUSTERED BY (${cols}) INTO ${options.clusteredBy.buckets} BUCKETS`;
|
|
189
|
+
}
|
|
190
|
+
if (options?.location) {
|
|
191
|
+
sql += ` LOCATION ${this.escape(options.location)}`;
|
|
192
|
+
}
|
|
193
|
+
if (options?.comment) {
|
|
194
|
+
sql += ` COMMENT ${this.escape(options.comment)}`;
|
|
195
|
+
}
|
|
196
|
+
if (options?.tblproperties && Object.keys(options.tblproperties).length > 0) {
|
|
197
|
+
const props = Object.entries(options.tblproperties)
|
|
198
|
+
.map(([k, v]) => `'${k}'=${this.escape(v)}`)
|
|
199
|
+
.join(', ');
|
|
200
|
+
sql += ` TBLPROPERTIES (${props})`;
|
|
201
|
+
}
|
|
202
|
+
return sql;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Create a table using Delta/Spark grammar. Delegates SQL generation to
|
|
206
|
+
* {@link buildCreateTableSql} and executes it.
|
|
207
|
+
*/
|
|
208
|
+
async createTable(tableName, columns, options) {
|
|
209
|
+
await this.query(this.buildCreateTableSql(tableName, columns, options));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Build a `CREATE TABLE ... USING <format> AS SELECT ...` (CTAS) statement.
|
|
213
|
+
* The column list and types are inferred from the SELECT, so only the format
|
|
214
|
+
* and (optionally) partitioning/location apply.
|
|
215
|
+
*/
|
|
216
|
+
buildCreateTableAsSelect(tableName, selectQuery, options) {
|
|
217
|
+
let sql = 'CREATE TABLE';
|
|
218
|
+
if (options?.ifNotExists) {
|
|
219
|
+
sql += ' IF NOT EXISTS';
|
|
220
|
+
}
|
|
221
|
+
sql += ` ${this.escapeId(tableName)}`;
|
|
222
|
+
sql += ` USING ${options?.using || 'DELTA'}`;
|
|
223
|
+
if (options?.partitionedBy && options.partitionedBy.length > 0) {
|
|
224
|
+
sql += ` PARTITIONED BY (${options.partitionedBy.map((c) => this.escapeId(c)).join(', ')})`;
|
|
225
|
+
}
|
|
226
|
+
if (options?.location) {
|
|
227
|
+
sql += ` LOCATION ${this.escape(options.location)}`;
|
|
228
|
+
}
|
|
229
|
+
if (options?.comment) {
|
|
230
|
+
sql += ` COMMENT ${this.escape(options.comment)}`;
|
|
231
|
+
}
|
|
232
|
+
if (options?.tblproperties && Object.keys(options.tblproperties).length > 0) {
|
|
233
|
+
const props = Object.entries(options.tblproperties)
|
|
234
|
+
.map(([k, v]) => `'${k}'=${this.escape(v)}`)
|
|
235
|
+
.join(', ');
|
|
236
|
+
sql += ` TBLPROPERTIES (${props})`;
|
|
237
|
+
}
|
|
238
|
+
sql += ` AS ${selectQuery.trim().replace(/;\s*$/, '')}`;
|
|
239
|
+
return sql;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Build a Delta Lake `MERGE INTO` upsert. Delta has no MySQL-style
|
|
243
|
+
* `ON DUPLICATE KEY UPDATE`; instead the row(s) to upsert are supplied as an
|
|
244
|
+
* inline `source` relation and matched against the target on the conflict
|
|
245
|
+
* (key) columns:
|
|
246
|
+
*
|
|
247
|
+
* MERGE INTO `t` AS target
|
|
248
|
+
* USING (SELECT ? AS `id`, ? AS `name`) AS source
|
|
249
|
+
* ON target.`id` = source.`id`
|
|
250
|
+
* WHEN MATCHED THEN UPDATE SET target.`name` = source.`name`
|
|
251
|
+
* WHEN NOT MATCHED THEN INSERT (`id`, `name`) VALUES (source.`id`, source.`name`)
|
|
252
|
+
*
|
|
253
|
+
* @param tableName - Target table.
|
|
254
|
+
* @param values - Column/value pairs for the row to upsert.
|
|
255
|
+
* @param options - `conflictFields` picks the ON/match key columns (defaults
|
|
256
|
+
* to all columns); `updateOnDuplicate` restricts which columns the
|
|
257
|
+
* WHEN MATCHED branch updates (defaults to all non-key columns).
|
|
258
|
+
*/
|
|
259
|
+
buildUpsertQuery(tableName, values, options) {
|
|
260
|
+
if (options?.returning) {
|
|
261
|
+
// Delta MERGE does not support a RETURNING clause; re-select instead.
|
|
262
|
+
throw new Error('Databricks (Delta MERGE) does not support the RETURNING clause. Re-fetch the row after the upsert instead.');
|
|
263
|
+
}
|
|
264
|
+
const columns = Object.keys(values);
|
|
265
|
+
if (columns.length === 0) {
|
|
266
|
+
throw new Error('buildUpsertQuery requires at least one column to upsert.');
|
|
267
|
+
}
|
|
268
|
+
const processedValues = [];
|
|
269
|
+
// Build the inline source relation: SELECT ? AS `col`, ...
|
|
270
|
+
const sourceSelect = columns
|
|
271
|
+
.map((col) => {
|
|
272
|
+
const value = values[col];
|
|
273
|
+
if (value instanceof prorm_1.Literal) {
|
|
274
|
+
return `${value.val} AS ${this.escapeId(col)}`;
|
|
275
|
+
}
|
|
276
|
+
processedValues.push(value);
|
|
277
|
+
return `? AS ${this.escapeId(col)}`;
|
|
278
|
+
})
|
|
279
|
+
.join(', ');
|
|
280
|
+
const conflictFields = options?.conflictFields && options.conflictFields.length > 0
|
|
281
|
+
? options.conflictFields
|
|
282
|
+
: columns;
|
|
283
|
+
const onClause = conflictFields
|
|
284
|
+
.map((c) => `target.${this.escapeId(c)} = source.${this.escapeId(c)}`)
|
|
285
|
+
.join(' AND ');
|
|
286
|
+
// Columns to update when matched: explicit list, or everything that isn't
|
|
287
|
+
// part of the match key.
|
|
288
|
+
const updateFields = options?.updateOnDuplicate && options.updateOnDuplicate.length > 0
|
|
289
|
+
? options.updateOnDuplicate
|
|
290
|
+
: columns.filter((c) => !conflictFields.includes(c));
|
|
291
|
+
const target = this.escapeId(tableName);
|
|
292
|
+
let sql = `MERGE INTO ${target} AS target USING (SELECT ${sourceSelect}) AS source ON ${onClause}`;
|
|
293
|
+
if (updateFields.length > 0) {
|
|
294
|
+
const setClause = updateFields
|
|
295
|
+
.map((c) => `target.${this.escapeId(c)} = source.${this.escapeId(c)}`)
|
|
296
|
+
.join(', ');
|
|
297
|
+
sql += ` WHEN MATCHED THEN UPDATE SET ${setClause}`;
|
|
298
|
+
}
|
|
299
|
+
const insertCols = columns.map((c) => this.escapeId(c)).join(', ');
|
|
300
|
+
const insertVals = columns.map((c) => `source.${this.escapeId(c)}`).join(', ');
|
|
301
|
+
sql += ` WHEN NOT MATCHED THEN INSERT (${insertCols}) VALUES (${insertVals})`;
|
|
302
|
+
return { sql, values: processedValues };
|
|
303
|
+
}
|
|
304
|
+
// ---------------------------------------------------------------------------
|
|
305
|
+
// CRUD overrides — Spark SQL / Delta diverges from MySQL here.
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
/**
|
|
308
|
+
* Build a `LIMIT`/`OFFSET` tail. Spark SQL supports `LIMIT n` and, since
|
|
309
|
+
* Spark 3.4 / Databricks Runtime 11+, a standalone `OFFSET n` (with or
|
|
310
|
+
* without a preceding `LIMIT`). Unlike MySQL there is NO need for the
|
|
311
|
+
* `LIMIT 18446744073709551615 OFFSET n` sentinel trick — Spark accepts
|
|
312
|
+
* `OFFSET` on its own — so this override drops that MySQL-only hack.
|
|
313
|
+
*
|
|
314
|
+
* NOTE: `OFFSET` is only valid on the outermost query and requires a
|
|
315
|
+
* recent Spark/DBR; on older runtimes it raises a parser error. For deep
|
|
316
|
+
* pagination prefer a keyset/`WHERE` predicate over large offsets.
|
|
317
|
+
*/
|
|
318
|
+
buildLimitOffset(limit, offset) {
|
|
319
|
+
let sql = '';
|
|
320
|
+
if (limit !== undefined) {
|
|
321
|
+
sql += ` LIMIT ${Number(limit)}`;
|
|
322
|
+
}
|
|
323
|
+
if (offset !== undefined) {
|
|
324
|
+
sql += ` OFFSET ${Number(offset)}`;
|
|
325
|
+
}
|
|
326
|
+
return sql;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Build an INSERT. A plain insert (`INSERT INTO t (cols) VALUES (...)`) is
|
|
330
|
+
* valid Spark SQL and reuses the inherited MySQL builder verbatim. When the
|
|
331
|
+
* caller sets `upsert`, MySQL would emit `... ON DUPLICATE KEY UPDATE`, which
|
|
332
|
+
* Spark/Delta rejects — so route to a Delta `MERGE INTO` instead.
|
|
333
|
+
*/
|
|
334
|
+
buildInsertQuery(tableName, values, options) {
|
|
335
|
+
if (options?.upsert) {
|
|
336
|
+
return this.buildUpsertQuery(tableName, values, {
|
|
337
|
+
conflictFields: options.conflictFields,
|
|
338
|
+
updateOnDuplicate: options.updateOnDuplicate,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return super.buildInsertQuery(tableName, values);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Build an UPDATE. Delta Lake supports `UPDATE t SET ... WHERE ...`, but
|
|
345
|
+
* (unlike MySQL) it does NOT support a trailing `LIMIT` on UPDATE, nor a
|
|
346
|
+
* `RETURNING` clause. Strip `LIMIT` and reject `RETURNING` rather than emit
|
|
347
|
+
* SQL Spark would reject.
|
|
348
|
+
*/
|
|
349
|
+
buildUpdateQuery(tableName, values, where, options) {
|
|
350
|
+
if (options?.returning) {
|
|
351
|
+
throw new Error('Databricks (Delta) does not support RETURNING on UPDATE. Re-fetch the rows after the update instead.');
|
|
352
|
+
}
|
|
353
|
+
// Reuse the inherited builder but never emit LIMIT (Delta UPDATE has none).
|
|
354
|
+
return super.buildUpdateQuery(tableName, values, where, { ...options, limit: undefined });
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Build a DELETE. Delta Lake supports `DELETE FROM t WHERE ...`, but not a
|
|
358
|
+
* trailing `LIMIT` or `RETURNING`. Strip `LIMIT` and reject `RETURNING`.
|
|
359
|
+
*/
|
|
360
|
+
buildDeleteQuery(tableName, where, options) {
|
|
361
|
+
if (options?.returning) {
|
|
362
|
+
throw new Error('Databricks (Delta) does not support RETURNING on DELETE. Re-fetch/track the rows before deleting instead.');
|
|
363
|
+
}
|
|
364
|
+
return super.buildDeleteQuery(tableName, where, { ...options, limit: undefined });
|
|
365
|
+
}
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// DDL overrides — Spark ALTER TABLE grammar differs from MySQL.
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
/**
|
|
370
|
+
* Column spec for `ALTER TABLE ... ADD COLUMNS`. Spark only accepts
|
|
371
|
+
* `name type [COMMENT '...']` (optionally `FIRST`/`AFTER col`) here — it does
|
|
372
|
+
* NOT accept `NOT NULL`, `DEFAULT`, `PRIMARY KEY`, or identity clauses on an
|
|
373
|
+
* added column, so those are intentionally omitted (unlike the full CREATE
|
|
374
|
+
* TABLE column definition).
|
|
375
|
+
*/
|
|
376
|
+
buildAddColumnSpecSql(columnName, definition) {
|
|
377
|
+
let sql = `${this.escapeId(columnName)} ${this.getDataTypeSql(definition.type)}`;
|
|
378
|
+
if (definition.comment) {
|
|
379
|
+
sql += ` COMMENT ${this.escape(definition.comment)}`;
|
|
380
|
+
}
|
|
381
|
+
return sql;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Build `ALTER TABLE t ADD COLUMNS (col type ...)`. Spark uses the plural
|
|
385
|
+
* `ADD COLUMNS (...)` with a parenthesised list — NOT MySQL's singular
|
|
386
|
+
* `ADD COLUMN col ...`.
|
|
387
|
+
*/
|
|
388
|
+
buildAddColumnSql(tableName, columnName, definition) {
|
|
389
|
+
return `ALTER TABLE ${this.escapeId(tableName)} ADD COLUMNS (${this.buildAddColumnSpecSql(columnName, definition)})`;
|
|
390
|
+
}
|
|
391
|
+
async addColumn(tableName, columnName, definition) {
|
|
392
|
+
await this.query(this.buildAddColumnSql(tableName, columnName, definition));
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Build `ALTER TABLE t DROP COLUMN col`. Valid Delta grammar (requires Delta
|
|
396
|
+
* column-mapping mode to be enabled on the table); identical in shape to
|
|
397
|
+
* MySQL, so behaviourally inherited but surfaced as a sync builder for tests.
|
|
398
|
+
*/
|
|
399
|
+
buildDropColumnSql(tableName, columnName) {
|
|
400
|
+
return `ALTER TABLE ${this.escapeId(tableName)} DROP COLUMN ${this.escapeId(columnName)}`;
|
|
401
|
+
}
|
|
402
|
+
async removeColumn(tableName, columnName) {
|
|
403
|
+
await this.query(this.buildDropColumnSql(tableName, columnName));
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Build `ALTER TABLE t RENAME COLUMN old TO new`. Spark/Delta uses
|
|
407
|
+
* `RENAME COLUMN`; MySQL uses `CHANGE old new <type>` (which also requires
|
|
408
|
+
* re-stating the type). This override needs no `DESCRIBE` round-trip.
|
|
409
|
+
*/
|
|
410
|
+
buildRenameColumnSql(tableName, oldColumnName, newColumnName) {
|
|
411
|
+
return `ALTER TABLE ${this.escapeId(tableName)} RENAME COLUMN ${this.escapeId(oldColumnName)} TO ${this.escapeId(newColumnName)}`;
|
|
412
|
+
}
|
|
413
|
+
async renameColumn(tableName, oldColumnName, newColumnName) {
|
|
414
|
+
await this.query(this.buildRenameColumnSql(tableName, oldColumnName, newColumnName));
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Delta only supports a narrow set of in-place column alterations
|
|
418
|
+
* (comment, nullability, ordering — and, on recent runtimes, limited type
|
|
419
|
+
* widening). MySQL's `CHANGE col <full-def>` is not valid Spark SQL, so
|
|
420
|
+
* rather than emit something a cluster would reject, expose the two
|
|
421
|
+
* supported primitives explicitly and fail fast on the generic path.
|
|
422
|
+
*/
|
|
423
|
+
async changeColumn(_tableName, _columnName, _definition) {
|
|
424
|
+
throw new Error('Databricks (Delta) does not support MySQL-style CHANGE COLUMN. Use renameColumn(), ' +
|
|
425
|
+
'setColumnComment(), or setColumnNullability() for the supported in-place alterations; ' +
|
|
426
|
+
'arbitrary type changes require rewriting the table.');
|
|
427
|
+
}
|
|
428
|
+
/** Build `ALTER TABLE t ALTER COLUMN col COMMENT '...'` (Delta-supported). */
|
|
429
|
+
buildSetColumnCommentSql(tableName, columnName, comment) {
|
|
430
|
+
return `ALTER TABLE ${this.escapeId(tableName)} ALTER COLUMN ${this.escapeId(columnName)} COMMENT ${this.escape(comment)}`;
|
|
431
|
+
}
|
|
432
|
+
async setColumnComment(tableName, columnName, comment) {
|
|
433
|
+
await this.query(this.buildSetColumnCommentSql(tableName, columnName, comment));
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Build `ALTER TABLE t ALTER COLUMN col SET|DROP NOT NULL` (Delta-supported).
|
|
437
|
+
*/
|
|
438
|
+
buildSetColumnNullabilitySql(tableName, columnName, notNull) {
|
|
439
|
+
const action = notNull ? 'SET NOT NULL' : 'DROP NOT NULL';
|
|
440
|
+
return `ALTER TABLE ${this.escapeId(tableName)} ALTER COLUMN ${this.escapeId(columnName)} ${action}`;
|
|
441
|
+
}
|
|
442
|
+
async setColumnNullability(tableName, columnName, notNull) {
|
|
443
|
+
await this.query(this.buildSetColumnNullabilitySql(tableName, columnName, notNull));
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Build `DROP TABLE [IF EXISTS] t`. Spark has no `CASCADE` on `DROP TABLE`
|
|
447
|
+
* (that's a MySQL/Postgres construct), so the inherited CASCADE handling is
|
|
448
|
+
* overridden away.
|
|
449
|
+
*/
|
|
450
|
+
buildDropTableSql(tableName, options) {
|
|
451
|
+
let sql = 'DROP TABLE';
|
|
452
|
+
if (options?.ifExists) {
|
|
453
|
+
sql += ' IF EXISTS';
|
|
454
|
+
}
|
|
455
|
+
sql += ` ${this.escapeId(tableName)}`;
|
|
456
|
+
return sql;
|
|
457
|
+
}
|
|
458
|
+
async dropTable(tableName, options) {
|
|
459
|
+
await this.query(this.buildDropTableSql(tableName, options));
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Build `ALTER TABLE t SET TBLPROPERTIES ('k'='v', ...)` — the Delta/Spark
|
|
463
|
+
* way to change table-level options after creation.
|
|
464
|
+
*/
|
|
465
|
+
buildSetTablePropertiesSql(tableName, properties) {
|
|
466
|
+
const props = Object.entries(properties)
|
|
467
|
+
.map(([k, v]) => `'${k}'=${this.escape(v)}`)
|
|
468
|
+
.join(', ');
|
|
469
|
+
return `ALTER TABLE ${this.escapeId(tableName)} SET TBLPROPERTIES (${props})`;
|
|
470
|
+
}
|
|
471
|
+
async setTableProperties(tableName, properties) {
|
|
472
|
+
await this.query(this.buildSetTablePropertiesSql(tableName, properties));
|
|
473
|
+
}
|
|
474
|
+
/** Build `ALTER TABLE t UNSET TBLPROPERTIES [IF EXISTS] ('k', ...)`. */
|
|
475
|
+
buildUnsetTablePropertiesSql(tableName, keys, ifExists = false) {
|
|
476
|
+
const list = keys.map((k) => `'${k}'`).join(', ');
|
|
477
|
+
return `ALTER TABLE ${this.escapeId(tableName)} UNSET TBLPROPERTIES ${ifExists ? 'IF EXISTS ' : ''}(${list})`;
|
|
478
|
+
}
|
|
479
|
+
async unsetTableProperties(tableName, keys, ifExists = false) {
|
|
480
|
+
await this.query(this.buildUnsetTablePropertiesSql(tableName, keys, ifExists));
|
|
481
|
+
}
|
|
482
|
+
async addIndex(_tableName, _indexName, _fields, _options) {
|
|
483
|
+
throw new Error(DatabricksDialect.INDEX_UNSUPPORTED);
|
|
484
|
+
}
|
|
485
|
+
async createIndex(_tableName, _indexDef) {
|
|
486
|
+
throw new Error(DatabricksDialect.INDEX_UNSUPPORTED);
|
|
487
|
+
}
|
|
488
|
+
async removeIndex(_tableName, _indexName) {
|
|
489
|
+
throw new Error(DatabricksDialect.INDEX_UNSUPPORTED);
|
|
490
|
+
}
|
|
491
|
+
async dropIndex(_tableName, _indexName) {
|
|
492
|
+
throw new Error(DatabricksDialect.INDEX_UNSUPPORTED);
|
|
493
|
+
}
|
|
494
|
+
async createFulltextIndex() {
|
|
495
|
+
throw new Error(DatabricksDialect.INDEX_UNSUPPORTED);
|
|
496
|
+
}
|
|
497
|
+
async createSpatialIndex() {
|
|
498
|
+
throw new Error(DatabricksDialect.INDEX_UNSUPPORTED);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
exports.DatabricksDialect = DatabricksDialect;
|
|
502
|
+
// ---------------------------------------------------------------------------
|
|
503
|
+
// Secondary indexes — unsupported by Spark SQL / Delta.
|
|
504
|
+
// ---------------------------------------------------------------------------
|
|
505
|
+
// Spark SQL has no generic `CREATE INDEX`. Databricks offers only specialized
|
|
506
|
+
// constructs (e.g. Bloom-filter indexes, Z-ORDER / liquid clustering) that
|
|
507
|
+
// don't map to the ORM's index API. Fail fast instead of emitting the
|
|
508
|
+
// inherited MySQL `CREATE INDEX ... ON ...`, which a cluster would reject.
|
|
509
|
+
DatabricksDialect.INDEX_UNSUPPORTED = 'Databricks (Spark SQL / Delta) does not support generic secondary indexes ' +
|
|
510
|
+
'(CREATE INDEX). Use liquid clustering / Z-ORDER (OPTIMIZE ... ZORDER BY), ' +
|
|
511
|
+
'partitioning, or a Bloom-filter index instead.';
|
|
512
|
+
/**
|
|
513
|
+
* Convenience factory mirroring the other dialects' `create*Dialect` helpers.
|
|
514
|
+
*/
|
|
515
|
+
function createDatabricksDialect(options) {
|
|
516
|
+
return new DatabricksDialect(options || {});
|
|
517
|
+
}
|