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,427 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-dialect, typed JSON-path function API.
|
|
4
|
+
*
|
|
5
|
+
* This module lets callers pull a JSON field/path out of a JSON/JSONB column
|
|
6
|
+
* (or any expression producing one) as a first-class, typed SQL expression
|
|
7
|
+
* usable in `attributes` / `SELECT`, without ever having to hand-write raw
|
|
8
|
+
* SQL strings for it.
|
|
9
|
+
*
|
|
10
|
+
* It is intentionally self-contained: it does not rely on (or modify) the
|
|
11
|
+
* dialect classes in `src/dialects/**` or the compiler in
|
|
12
|
+
* `src/query-builders/sql-compiler.ts`. Instead it re-derives, per dialect,
|
|
13
|
+
* the exact SQL fragments that this codebase's own dialect-internal
|
|
14
|
+
* WHERE-clause JSON helpers already produce (see the JSDoc on each function
|
|
15
|
+
* below for the specific dialect file/method that was used as the source of
|
|
16
|
+
* truth), so behavior stays consistent with the rest of the ORM.
|
|
17
|
+
*
|
|
18
|
+
* All functions return a `{ $literal: string }` value (the same shape
|
|
19
|
+
* produced by `literal()` in `src/operators.ts`), so the result can be
|
|
20
|
+
* dropped directly into `attributes`, e.g.:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* User.findAll({
|
|
24
|
+
* attributes: ['id', [jsonExtract('postgres', 'data', 'address.city'), 'city']],
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.jsonExtract = jsonExtract;
|
|
30
|
+
exports.jsonContains = jsonContains;
|
|
31
|
+
exports.jsonHasKey = jsonHasKey;
|
|
32
|
+
exports.jsonKeys = jsonKeys;
|
|
33
|
+
exports.jsonTypeOf = jsonTypeOf;
|
|
34
|
+
const operators_1 = require("../../operators");
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Local, self-contained SQL-fragment rendering helpers
|
|
37
|
+
// (deliberately not imported from sql-compiler.ts, per architecture constraints)
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
function quoteChars(dialect) {
|
|
40
|
+
switch (dialect) {
|
|
41
|
+
case 'mysql':
|
|
42
|
+
case 'mariadb':
|
|
43
|
+
case 'clickhouse':
|
|
44
|
+
return { open: '`', close: '`', escape: (s) => s.replace(/`/g, '``') };
|
|
45
|
+
case 'mssql':
|
|
46
|
+
return { open: '[', close: ']', escape: (s) => s.replace(/\]/g, ']]') };
|
|
47
|
+
default:
|
|
48
|
+
// postgres, sqlite, cockroachdb, redshift, db2, snowflake, oracle
|
|
49
|
+
return { open: '"', close: '"', escape: (s) => s.replace(/"/g, '""') };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Escape/quote a single identifier segment (no dot-splitting). */
|
|
53
|
+
function quoteIdentifierPart(dialect, part) {
|
|
54
|
+
const { open, close, escape } = quoteChars(dialect);
|
|
55
|
+
return `${open}${escape(part)}${close}`;
|
|
56
|
+
}
|
|
57
|
+
/** Escape/quote a possibly dotted identifier path (e.g. `table.column`). */
|
|
58
|
+
function quoteIdentifierPath(dialect, name) {
|
|
59
|
+
return name
|
|
60
|
+
.split('.')
|
|
61
|
+
.map((part) => quoteIdentifierPart(dialect, part))
|
|
62
|
+
.join('.');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Escape a string literal for embedding directly into generated SQL text.
|
|
66
|
+
* MySQL/MariaDB also need backslash escaping (in addition to quote doubling);
|
|
67
|
+
* every other supported dialect here only needs `'` doubled.
|
|
68
|
+
*/
|
|
69
|
+
function escapeStringLiteral(dialect, value) {
|
|
70
|
+
if (dialect === 'mysql' || dialect === 'mariadb') {
|
|
71
|
+
return value.replace(/\\/g, '\\\\').replace(/'/g, "''");
|
|
72
|
+
}
|
|
73
|
+
return value.replace(/'/g, "''");
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Render any supported expression argument (plain column name string,
|
|
77
|
+
* `col()`, `fn()`, or `literal()`/`{ $literal }`) down to a raw SQL fragment
|
|
78
|
+
* string, using this module's own dialect-aware identifier quoting.
|
|
79
|
+
*/
|
|
80
|
+
function toSqlFragment(dialect, arg) {
|
|
81
|
+
if (arg !== null && typeof arg === 'object') {
|
|
82
|
+
if ('$literal' in arg) {
|
|
83
|
+
return arg.$literal;
|
|
84
|
+
}
|
|
85
|
+
const typed = arg;
|
|
86
|
+
if (typed.__type === 'literal') {
|
|
87
|
+
return arg.sql;
|
|
88
|
+
}
|
|
89
|
+
if (typed.__type === 'col') {
|
|
90
|
+
return quoteIdentifierPath(dialect, arg.col);
|
|
91
|
+
}
|
|
92
|
+
if (typed.__type === 'fn') {
|
|
93
|
+
const fnExpr = arg;
|
|
94
|
+
const renderedArgs = fnExpr.args.map((a) => toSqlFragment(dialect, a)).join(', ');
|
|
95
|
+
return `${fnExpr.fn}(${renderedArgs})`;
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`jsonExtract/jsonContains/jsonHasKey/jsonKeys/jsonTypeOf: unsupported expression argument: ${JSON.stringify(arg)}`);
|
|
98
|
+
}
|
|
99
|
+
if (typeof arg === 'string') {
|
|
100
|
+
// Plain strings are treated as column names (consistent with col()).
|
|
101
|
+
return quoteIdentifierPath(dialect, arg);
|
|
102
|
+
}
|
|
103
|
+
throw new Error(`jsonExtract: unsupported expression argument: ${String(arg)}`);
|
|
104
|
+
}
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Path normalization
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
/** Normalize `path` (dotted string or array of keys/indices) to plain string segments. */
|
|
109
|
+
function normalizePathSegments(path) {
|
|
110
|
+
if (Array.isArray(path)) {
|
|
111
|
+
return path.map((p) => String(p));
|
|
112
|
+
}
|
|
113
|
+
// Strip a leading '$.' / '$' so callers can pass either 'a.b' or '$.a.b'.
|
|
114
|
+
const stripped = path.startsWith('$.') ? path.slice(2) : path.startsWith('$') ? path.slice(1) : path;
|
|
115
|
+
return stripped.split('.').filter((s) => s.length > 0);
|
|
116
|
+
}
|
|
117
|
+
const isArrayIndexSegment = (seg) => /^\d+$/.test(seg);
|
|
118
|
+
/** Build a MySQL/MariaDB/MSSQL/SQLite/Oracle/Db2-style `$.a.b[0]` JSON path string. */
|
|
119
|
+
function buildDollarPath(segments) {
|
|
120
|
+
let out = '$';
|
|
121
|
+
for (const seg of segments) {
|
|
122
|
+
out += isArrayIndexSegment(seg) ? `[${seg}]` : `.${seg}`;
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
/** Build a Postgres/CockroachDB `#>`/`#>>` style `'{a,b}'` path-array literal body. */
|
|
127
|
+
function buildPgPathArray(segments) {
|
|
128
|
+
return segments.join(',');
|
|
129
|
+
}
|
|
130
|
+
/** Build a Redshift SUPER-navigation dot path: `a.b` or `a[0].b`. */
|
|
131
|
+
function buildDotPath(segments) {
|
|
132
|
+
return segments
|
|
133
|
+
.map((seg, i) => (isArrayIndexSegment(seg) ? `[${seg}]` : i === 0 ? seg : `.${seg}`))
|
|
134
|
+
.join('');
|
|
135
|
+
}
|
|
136
|
+
/** Build a Snowflake VARIANT `:`-path: `:a.b` or `:a[0].b`. */
|
|
137
|
+
function buildSnowflakePath(segments) {
|
|
138
|
+
return segments
|
|
139
|
+
.map((seg, i) => (isArrayIndexSegment(seg) ? `[${seg}]` : i === 0 ? `:${seg}` : `.${seg}`))
|
|
140
|
+
.join('');
|
|
141
|
+
}
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// jsonExtract
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
/**
|
|
146
|
+
* Extract a value at a JSON path from `expr` as a typed, cross-dialect SQL
|
|
147
|
+
* expression usable in `attributes` / `SELECT`.
|
|
148
|
+
*
|
|
149
|
+
* Dialect syntax sources (matched against this codebase's existing internal
|
|
150
|
+
* dialect helpers where they exist):
|
|
151
|
+
* - mysql/mariadb: `src/dialects/{mysql,mariadb}/index.ts` `buildJsonExtract` ->
|
|
152
|
+
* `JSON_UNQUOTE(JSON_EXTRACT(col, '$.path'))` (asText) / `JSON_EXTRACT(col, '$.path')`.
|
|
153
|
+
* - mssql: `src/dialects/mssql/index.ts` `buildJsonExtract` ->
|
|
154
|
+
* `JSON_VALUE(col, '$.path')` (asText) / `JSON_QUERY(col, '$.path')`.
|
|
155
|
+
* - sqlite: `src/dialects/sqlite/index.ts` `buildJsonExtract` ->
|
|
156
|
+
* `CAST(json_extract(col, '$.path') AS TEXT)` (asText) / `json_extract(col, '$.path')`.
|
|
157
|
+
* - postgres/cockroachdb: `src/query-builders/where-builder.ts` `#>>`/`#>` handling ->
|
|
158
|
+
* `col #>> '{a,b}'` (asText) / `col #> '{a,b}'`.
|
|
159
|
+
* - oracle: Oracle SQL/JSON functions -> `JSON_VALUE(col, '$.path')` (asText) /
|
|
160
|
+
* `JSON_QUERY(col, '$.path')`.
|
|
161
|
+
* - snowflake: `src/dialects/snowflake/index.ts` `buildJsonQuery` -> `col:a.b::STRING`
|
|
162
|
+
* (asText) / `col:a.b` (VARIANT, non-text).
|
|
163
|
+
* - redshift: SUPER-type dot navigation (see `getDataTypeSql` mapping JSON/JSONB -> SUPER
|
|
164
|
+
* in `src/dialects/redshift/index.ts`) -> `CAST(col.a.b AS VARCHAR)` (asText) / `col.a.b`.
|
|
165
|
+
* - db2: Db2 JSON_VAL/JSON_QUERY path functions -> `JSON_VAL(col, 'a.b')` (asText) /
|
|
166
|
+
* `JSON_QUERY(col, '$.a.b')`.
|
|
167
|
+
* - clickhouse: `JSONExtractString(col, 'a', 'b')` (asText) / `JSONExtractRaw(col, 'a', 'b')`.
|
|
168
|
+
*/
|
|
169
|
+
function jsonExtract(dialect, expr, path, options = {}) {
|
|
170
|
+
const asText = options.asText !== false;
|
|
171
|
+
const columnRef = toSqlFragment(dialect, expr);
|
|
172
|
+
const segments = normalizePathSegments(path);
|
|
173
|
+
switch (dialect) {
|
|
174
|
+
case 'mysql':
|
|
175
|
+
case 'mariadb': {
|
|
176
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
177
|
+
const extracted = `JSON_EXTRACT(${columnRef}, '${dollarPath}')`;
|
|
178
|
+
return (0, operators_1.literal)(asText ? `JSON_UNQUOTE(${extracted})` : extracted);
|
|
179
|
+
}
|
|
180
|
+
case 'mssql': {
|
|
181
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
182
|
+
return (0, operators_1.literal)(asText ? `JSON_VALUE(${columnRef}, '${dollarPath}')` : `JSON_QUERY(${columnRef}, '${dollarPath}')`);
|
|
183
|
+
}
|
|
184
|
+
case 'sqlite': {
|
|
185
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
186
|
+
const extracted = `json_extract(${columnRef}, '${dollarPath}')`;
|
|
187
|
+
return (0, operators_1.literal)(asText ? `CAST(${extracted} AS TEXT)` : extracted);
|
|
188
|
+
}
|
|
189
|
+
case 'postgres':
|
|
190
|
+
case 'cockroachdb': {
|
|
191
|
+
const pgPath = escapeStringLiteral(dialect, buildPgPathArray(segments));
|
|
192
|
+
return (0, operators_1.literal)(asText ? `${columnRef} #>> '{${pgPath}}'` : `${columnRef} #> '{${pgPath}}'`);
|
|
193
|
+
}
|
|
194
|
+
case 'oracle': {
|
|
195
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
196
|
+
return (0, operators_1.literal)(asText ? `JSON_VALUE(${columnRef}, '${dollarPath}')` : `JSON_QUERY(${columnRef}, '${dollarPath}')`);
|
|
197
|
+
}
|
|
198
|
+
case 'snowflake': {
|
|
199
|
+
const snowPath = buildSnowflakePath(segments);
|
|
200
|
+
const extracted = `${columnRef}${snowPath}`;
|
|
201
|
+
return (0, operators_1.literal)(asText ? `${extracted}::STRING` : extracted);
|
|
202
|
+
}
|
|
203
|
+
case 'redshift': {
|
|
204
|
+
const dotPath = buildDotPath(segments);
|
|
205
|
+
const extracted = `${columnRef}.${dotPath}`;
|
|
206
|
+
return (0, operators_1.literal)(asText ? `CAST(${extracted} AS VARCHAR)` : extracted);
|
|
207
|
+
}
|
|
208
|
+
case 'db2': {
|
|
209
|
+
// Db2 stores JSON as text (JSON DataType maps to CLOB, see
|
|
210
|
+
// src/dialects/db2/index.ts getDataTypeSql), so JSON_VAL/JSON_QUERY
|
|
211
|
+
// operate over the CLOB/VARCHAR content.
|
|
212
|
+
const dotPath = escapeStringLiteral(dialect, segments.join('.'));
|
|
213
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
214
|
+
return (0, operators_1.literal)(asText ? `JSON_VAL(${columnRef}, '${dotPath}')` : `JSON_QUERY(${columnRef}, '${dollarPath}')`);
|
|
215
|
+
}
|
|
216
|
+
case 'clickhouse': {
|
|
217
|
+
const args = [columnRef, ...segments.map((seg) => (isArrayIndexSegment(seg) ? seg : `'${escapeStringLiteral(dialect, seg)}'`))].join(', ');
|
|
218
|
+
return (0, operators_1.literal)(asText ? `JSONExtractString(${args})` : `JSONExtractRaw(${args})`);
|
|
219
|
+
}
|
|
220
|
+
default:
|
|
221
|
+
throw new Error(`jsonExtract: unsupported dialect '${dialect}'`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
// jsonContains
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
/**
|
|
228
|
+
* Does the JSON value in `expr` contain `value` (as a fragment/sub-document,
|
|
229
|
+
* or, where the dialect has no native containment predicate, as an exact
|
|
230
|
+
* equality check)?
|
|
231
|
+
*
|
|
232
|
+
* - mysql/mariadb: `JSON_CONTAINS(col, '<value-json>')` (matches
|
|
233
|
+
* `buildJsonContains` in `src/dialects/{mysql,mariadb}/index.ts`).
|
|
234
|
+
* - postgres/cockroachdb: native jsonb containment operator -> `col @> '<value-json>'::jsonb`.
|
|
235
|
+
* - sqlite: SQLite has no containment operator; mirrors the whole-document
|
|
236
|
+
* equality fallback used by `buildJsonContains` in
|
|
237
|
+
* `src/dialects/sqlite/index.ts` -> `col = '<value-json>'`.
|
|
238
|
+
* - mssql, oracle, snowflake, redshift, db2, clickhouse: none of these expose
|
|
239
|
+
* a native "does this JSON document contain this sub-fragment" predicate
|
|
240
|
+
* (as opposed to key existence or exact-path equality), so this throws a
|
|
241
|
+
* clear "not supported" error rather than emitting a plausible-looking but
|
|
242
|
+
* semantically wrong query.
|
|
243
|
+
*/
|
|
244
|
+
function jsonContains(dialect, expr, value) {
|
|
245
|
+
const columnRef = toSqlFragment(dialect, expr);
|
|
246
|
+
const jsonText = escapeStringLiteral(dialect, JSON.stringify(value));
|
|
247
|
+
switch (dialect) {
|
|
248
|
+
case 'mysql':
|
|
249
|
+
case 'mariadb':
|
|
250
|
+
return (0, operators_1.literal)(`JSON_CONTAINS(${columnRef}, '${jsonText}')`);
|
|
251
|
+
case 'postgres':
|
|
252
|
+
case 'cockroachdb':
|
|
253
|
+
return (0, operators_1.literal)(`${columnRef} @> '${jsonText}'::jsonb`);
|
|
254
|
+
case 'sqlite':
|
|
255
|
+
// SQLite has no jsonb-style containment operator; the dialect's own
|
|
256
|
+
// buildJsonContains() falls back to whole-document equality.
|
|
257
|
+
return (0, operators_1.literal)(`${columnRef} = '${jsonText}'`);
|
|
258
|
+
case 'mssql':
|
|
259
|
+
case 'oracle':
|
|
260
|
+
case 'snowflake':
|
|
261
|
+
case 'redshift':
|
|
262
|
+
case 'db2':
|
|
263
|
+
case 'clickhouse':
|
|
264
|
+
throw new Error(`jsonContains: not supported for dialect '${dialect}' - this dialect has no native JSON containment predicate (only key existence / exact path equality), so a correct containment query cannot be generated here.`);
|
|
265
|
+
default:
|
|
266
|
+
throw new Error(`jsonContains: unsupported dialect '${dialect}'`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// jsonHasKey
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
/**
|
|
273
|
+
* Does the JSON object in `expr` have the given top-level (or path-qualified,
|
|
274
|
+
* dotted) key?
|
|
275
|
+
*
|
|
276
|
+
* - mysql/mariadb: `JSON_CONTAINS_PATH(col, 'one', '$.key')` (matches
|
|
277
|
+
* `buildJsonHasKey` in `src/dialects/{mysql,mariadb}/index.ts`).
|
|
278
|
+
* - postgres/cockroachdb: native `?` key-existence operator -> `col ? 'key'`
|
|
279
|
+
* (top-level keys only, matching jsonb semantics).
|
|
280
|
+
* - sqlite: `json_extract(col, '$.key') IS NOT NULL` (matches
|
|
281
|
+
* `buildJsonHasKey` in `src/dialects/sqlite/index.ts`).
|
|
282
|
+
* - mssql: `JSON_VALUE(col, '$.key') IS NOT NULL` (standard SQL Server
|
|
283
|
+
* workaround pattern; note this cannot distinguish "key present with a
|
|
284
|
+
* JSON null value" from "key absent").
|
|
285
|
+
* - oracle: `JSON_EXISTS(col, '$.key')`.
|
|
286
|
+
* - snowflake: `ARRAY_CONTAINS('key'::VARIANT, OBJECT_KEYS(col))`.
|
|
287
|
+
* - redshift: SUPER dot-navigation returns SQL NULL for missing attributes,
|
|
288
|
+
* so `col.key IS NOT NULL` reliably detects key presence.
|
|
289
|
+
* - db2: `JSON_EXISTS(col, '$.key')` (Db2 11.5+ JSON path predicates).
|
|
290
|
+
* - clickhouse: `JSONHas(col, 'key')`.
|
|
291
|
+
*/
|
|
292
|
+
function jsonHasKey(dialect, expr, key) {
|
|
293
|
+
const columnRef = toSqlFragment(dialect, expr);
|
|
294
|
+
const segments = normalizePathSegments(key);
|
|
295
|
+
switch (dialect) {
|
|
296
|
+
case 'mysql':
|
|
297
|
+
case 'mariadb': {
|
|
298
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
299
|
+
return (0, operators_1.literal)(`JSON_CONTAINS_PATH(${columnRef}, 'one', '${dollarPath}')`);
|
|
300
|
+
}
|
|
301
|
+
case 'postgres':
|
|
302
|
+
case 'cockroachdb': {
|
|
303
|
+
if (segments.length !== 1) {
|
|
304
|
+
throw new Error(`jsonHasKey: not supported for dialect '${dialect}' with a nested/dotted key ('${key}') - the '?' key-existence operator only tests top-level keys.`);
|
|
305
|
+
}
|
|
306
|
+
return (0, operators_1.literal)(`${columnRef} ? '${escapeStringLiteral(dialect, segments[0])}'`);
|
|
307
|
+
}
|
|
308
|
+
case 'sqlite': {
|
|
309
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
310
|
+
return (0, operators_1.literal)(`json_extract(${columnRef}, '${dollarPath}') IS NOT NULL`);
|
|
311
|
+
}
|
|
312
|
+
case 'mssql': {
|
|
313
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
314
|
+
return (0, operators_1.literal)(`JSON_VALUE(${columnRef}, '${dollarPath}') IS NOT NULL`);
|
|
315
|
+
}
|
|
316
|
+
case 'oracle':
|
|
317
|
+
case 'db2': {
|
|
318
|
+
const dollarPath = escapeStringLiteral(dialect, buildDollarPath(segments));
|
|
319
|
+
return (0, operators_1.literal)(`JSON_EXISTS(${columnRef}, '${dollarPath}')`);
|
|
320
|
+
}
|
|
321
|
+
case 'snowflake': {
|
|
322
|
+
if (segments.length !== 1) {
|
|
323
|
+
throw new Error(`jsonHasKey: not supported for dialect '${dialect}' with a nested/dotted key ('${key}') - OBJECT_KEYS() only enumerates the top-level keys of a VARIANT/OBJECT.`);
|
|
324
|
+
}
|
|
325
|
+
return (0, operators_1.literal)(`ARRAY_CONTAINS('${escapeStringLiteral(dialect, segments[0])}'::VARIANT, OBJECT_KEYS(${columnRef}))`);
|
|
326
|
+
}
|
|
327
|
+
case 'redshift': {
|
|
328
|
+
const dotPath = buildDotPath(segments);
|
|
329
|
+
return (0, operators_1.literal)(`${columnRef}.${dotPath} IS NOT NULL`);
|
|
330
|
+
}
|
|
331
|
+
case 'clickhouse': {
|
|
332
|
+
const args = [columnRef, ...segments.map((seg) => (isArrayIndexSegment(seg) ? seg : `'${escapeStringLiteral(dialect, seg)}'`))].join(', ');
|
|
333
|
+
return (0, operators_1.literal)(`JSONHas(${args})`);
|
|
334
|
+
}
|
|
335
|
+
default:
|
|
336
|
+
throw new Error(`jsonHasKey: unsupported dialect '${dialect}'`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
// jsonKeys
|
|
341
|
+
// ---------------------------------------------------------------------------
|
|
342
|
+
/**
|
|
343
|
+
* Get the list of top-level keys of a JSON object.
|
|
344
|
+
*
|
|
345
|
+
* - mysql/mariadb: `JSON_KEYS(col)` -> native, returns a JSON array of keys.
|
|
346
|
+
* - postgres/cockroachdb: `ARRAY(SELECT jsonb_object_keys(col))` -> a SQL
|
|
347
|
+
* text array of keys.
|
|
348
|
+
* - sqlite: `(SELECT json_group_array(key) FROM json_each(col))` -> a JSON
|
|
349
|
+
* array of keys, built from SQLite's own `json_each` table-valued function.
|
|
350
|
+
* - snowflake: `OBJECT_KEYS(col)` -> native, returns an ARRAY of keys.
|
|
351
|
+
* - clickhouse: `JSONExtractKeys(col)` -> native, returns an Array(String).
|
|
352
|
+
* - mssql: no native function returns a JSON/array value of keys inline;
|
|
353
|
+
* `OPENJSON` requires a derived table, so the closest inline expression is
|
|
354
|
+
* a scalar subquery producing a comma-separated string (NOT a JSON array):
|
|
355
|
+
* `(SELECT STRING_AGG([key], ',') FROM OPENJSON(col))`.
|
|
356
|
+
* - oracle, redshift, db2: throw "not supported" - none of these dialects
|
|
357
|
+
* expose a documented function/operator that enumerates JSON object keys
|
|
358
|
+
* as a value usable inline in a SELECT list, so faking one with a fragile
|
|
359
|
+
* workaround would be worse than a clear error.
|
|
360
|
+
*/
|
|
361
|
+
function jsonKeys(dialect, expr) {
|
|
362
|
+
const columnRef = toSqlFragment(dialect, expr);
|
|
363
|
+
switch (dialect) {
|
|
364
|
+
case 'mysql':
|
|
365
|
+
case 'mariadb':
|
|
366
|
+
return (0, operators_1.literal)(`JSON_KEYS(${columnRef})`);
|
|
367
|
+
case 'postgres':
|
|
368
|
+
case 'cockroachdb':
|
|
369
|
+
return (0, operators_1.literal)(`ARRAY(SELECT jsonb_object_keys(${columnRef}))`);
|
|
370
|
+
case 'sqlite':
|
|
371
|
+
return (0, operators_1.literal)(`(SELECT json_group_array(key) FROM json_each(${columnRef}))`);
|
|
372
|
+
case 'snowflake':
|
|
373
|
+
return (0, operators_1.literal)(`OBJECT_KEYS(${columnRef})`);
|
|
374
|
+
case 'clickhouse':
|
|
375
|
+
return (0, operators_1.literal)(`JSONExtractKeys(${columnRef})`);
|
|
376
|
+
case 'mssql':
|
|
377
|
+
// Note: returns a comma-separated string of keys, not a JSON array,
|
|
378
|
+
// since T-SQL has no scalar function that produces one directly.
|
|
379
|
+
return (0, operators_1.literal)(`(SELECT STRING_AGG(CAST([key] AS NVARCHAR(MAX)), ',') FROM OPENJSON(${columnRef}))`);
|
|
380
|
+
case 'oracle':
|
|
381
|
+
case 'redshift':
|
|
382
|
+
case 'db2':
|
|
383
|
+
throw new Error(`jsonKeys: not supported for dialect '${dialect}' - no documented function/operator enumerates JSON object keys as an inline SELECT-list expression for this dialect.`);
|
|
384
|
+
default:
|
|
385
|
+
throw new Error(`jsonKeys: unsupported dialect '${dialect}'`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// ---------------------------------------------------------------------------
|
|
389
|
+
// jsonTypeOf
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
/**
|
|
392
|
+
* Get the JSON value's type (object/array/string/number/boolean/null/...).
|
|
393
|
+
*
|
|
394
|
+
* - mysql/mariadb: `JSON_TYPE(col)` -> native.
|
|
395
|
+
* - postgres/cockroachdb: `jsonb_typeof(col)` -> native.
|
|
396
|
+
* - sqlite: `json_type(col)` -> native.
|
|
397
|
+
* - snowflake: `TYPEOF(col)` -> native (works on VARIANT).
|
|
398
|
+
* - clickhouse: `JSONType(col)` -> native.
|
|
399
|
+
* - mssql, oracle, redshift, db2: throw "not supported" - none of these
|
|
400
|
+
* expose a documented function that returns the JSON value's type as a
|
|
401
|
+
* string (SQL Server's `ISJSON(col, 'object'|'array')` only returns a
|
|
402
|
+
* boolean for two of the possible types, not a general type name).
|
|
403
|
+
*/
|
|
404
|
+
function jsonTypeOf(dialect, expr) {
|
|
405
|
+
const columnRef = toSqlFragment(dialect, expr);
|
|
406
|
+
switch (dialect) {
|
|
407
|
+
case 'mysql':
|
|
408
|
+
case 'mariadb':
|
|
409
|
+
return (0, operators_1.literal)(`JSON_TYPE(${columnRef})`);
|
|
410
|
+
case 'postgres':
|
|
411
|
+
case 'cockroachdb':
|
|
412
|
+
return (0, operators_1.literal)(`jsonb_typeof(${columnRef})`);
|
|
413
|
+
case 'sqlite':
|
|
414
|
+
return (0, operators_1.literal)(`json_type(${columnRef})`);
|
|
415
|
+
case 'snowflake':
|
|
416
|
+
return (0, operators_1.literal)(`TYPEOF(${columnRef})`);
|
|
417
|
+
case 'clickhouse':
|
|
418
|
+
return (0, operators_1.literal)(`JSONType(${columnRef})`);
|
|
419
|
+
case 'mssql':
|
|
420
|
+
case 'oracle':
|
|
421
|
+
case 'redshift':
|
|
422
|
+
case 'db2':
|
|
423
|
+
throw new Error(`jsonTypeOf: not supported for dialect '${dialect}' - no documented function returns a general JSON value type name for this dialect.`);
|
|
424
|
+
default:
|
|
425
|
+
throw new Error(`jsonTypeOf: unsupported dialect '${dialect}'`);
|
|
426
|
+
}
|
|
427
|
+
}
|