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,426 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Query Batch/Bulk Optimization
|
|
4
|
+
* Provides functionality to optimize batch operations for better performance
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.BatchQueryOptimizer = void 0;
|
|
8
|
+
exports.optimizeBatchInsert = optimizeBatchInsert;
|
|
9
|
+
exports.optimizeBatchUpdate = optimizeBatchUpdate;
|
|
10
|
+
exports.optimizeBatchDelete = optimizeBatchDelete;
|
|
11
|
+
/**
|
|
12
|
+
* Batch query optimizer
|
|
13
|
+
*/
|
|
14
|
+
class BatchQueryOptimizer {
|
|
15
|
+
constructor(dialect, defaultOptions) {
|
|
16
|
+
this.dialect = dialect;
|
|
17
|
+
this.defaultOptions = {
|
|
18
|
+
chunkSize: 1000,
|
|
19
|
+
useTransaction: true,
|
|
20
|
+
validate: true,
|
|
21
|
+
skipDuplicates: false,
|
|
22
|
+
stopOnError: false,
|
|
23
|
+
...defaultOptions,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Set default options for batch operations
|
|
28
|
+
*/
|
|
29
|
+
setDefaultOptions(options) {
|
|
30
|
+
this.defaultOptions = { ...this.defaultOptions, ...options };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Optimize batch insert by combining multiple INSERT statements
|
|
34
|
+
* @param tableName - Name of the table to insert into
|
|
35
|
+
* @param records - Array of records to insert
|
|
36
|
+
* @param options - Batch options
|
|
37
|
+
*/
|
|
38
|
+
async optimizeBatchInsert(tableName, records, options) {
|
|
39
|
+
const opts = { ...this.defaultOptions, ...options };
|
|
40
|
+
const startTime = Date.now();
|
|
41
|
+
if (records.length === 0) {
|
|
42
|
+
return {
|
|
43
|
+
success: [],
|
|
44
|
+
failed: [],
|
|
45
|
+
total: 0,
|
|
46
|
+
chunkSize: opts.chunkSize,
|
|
47
|
+
batches: 0,
|
|
48
|
+
duration: 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const success = [];
|
|
52
|
+
const failed = [];
|
|
53
|
+
const chunks = this.chunkArray(records, opts.chunkSize);
|
|
54
|
+
const totalBatches = Math.ceil(records.length / opts.chunkSize);
|
|
55
|
+
for (let batchIndex = 0; batchIndex < chunks.length; batchIndex++) {
|
|
56
|
+
const chunk = chunks[batchIndex];
|
|
57
|
+
try {
|
|
58
|
+
// Try multi-value INSERT first (most efficient)
|
|
59
|
+
const result = await this.executeBatchInsert(tableName, chunk, opts);
|
|
60
|
+
success.push(...chunk);
|
|
61
|
+
this.logProgress(opts, {
|
|
62
|
+
total: records.length,
|
|
63
|
+
processed: success.length + failed.length,
|
|
64
|
+
success: success.length,
|
|
65
|
+
failed: failed.length,
|
|
66
|
+
batchIndex,
|
|
67
|
+
totalBatches,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (opts.stopOnError) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
// Fall back to individual inserts
|
|
75
|
+
for (const record of chunk) {
|
|
76
|
+
try {
|
|
77
|
+
await this.executeSingleInsert(tableName, record, opts);
|
|
78
|
+
success.push(record);
|
|
79
|
+
}
|
|
80
|
+
catch (individualError) {
|
|
81
|
+
if (!opts.skipDuplicates || !this.isDuplicateKeyError(individualError)) {
|
|
82
|
+
failed.push({ record, error: individualError });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.logProgress(opts, {
|
|
87
|
+
total: records.length,
|
|
88
|
+
processed: success.length + failed.length,
|
|
89
|
+
success: success.length,
|
|
90
|
+
failed: failed.length,
|
|
91
|
+
batchIndex,
|
|
92
|
+
totalBatches,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
success,
|
|
98
|
+
failed,
|
|
99
|
+
total: records.length,
|
|
100
|
+
chunkSize: opts.chunkSize,
|
|
101
|
+
batches: totalBatches,
|
|
102
|
+
duration: Date.now() - startTime,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Build the next placeholder token for a bound parameter, given the running
|
|
107
|
+
* 1-based parameter index. Matches the placeholder convention each dialect's
|
|
108
|
+
* own buildInsertQuery/buildUpdateQuery/buildDeleteQuery already uses:
|
|
109
|
+
* - postgres: $1, $2, ...
|
|
110
|
+
* - mssql: @p1, @p2, ...
|
|
111
|
+
* - oracle: :1, :2, ...
|
|
112
|
+
* - others (mysql/mariadb/sqlite): ?
|
|
113
|
+
*/
|
|
114
|
+
nextPlaceholder(paramIndex) {
|
|
115
|
+
switch (this.dialect.name) {
|
|
116
|
+
case 'postgres':
|
|
117
|
+
return `$${paramIndex}`;
|
|
118
|
+
case 'mssql':
|
|
119
|
+
return `@p${paramIndex}`;
|
|
120
|
+
case 'oracle':
|
|
121
|
+
return `:${paramIndex}`;
|
|
122
|
+
default:
|
|
123
|
+
return '?';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Execute a batch insert using multi-value INSERT
|
|
128
|
+
* Uses bound parameters with dialect-correct placeholders ($1, $2, ... for
|
|
129
|
+
* PostgreSQL, `?` for the other dialects) instead of interpolating values.
|
|
130
|
+
*/
|
|
131
|
+
async executeBatchInsert(tableName, records, options) {
|
|
132
|
+
if (records.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
const columns = Object.keys(records[0]);
|
|
135
|
+
const quotedTable = this.dialect.quoteTable(tableName);
|
|
136
|
+
const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(', ');
|
|
137
|
+
// Build multi-value INSERT
|
|
138
|
+
const valuesClauses = [];
|
|
139
|
+
const allParams = [];
|
|
140
|
+
let paramIndex = 1;
|
|
141
|
+
for (const record of records) {
|
|
142
|
+
const placeholders = [];
|
|
143
|
+
for (const col of columns) {
|
|
144
|
+
placeholders.push(this.nextPlaceholder(paramIndex++));
|
|
145
|
+
allParams.push(record[col]);
|
|
146
|
+
}
|
|
147
|
+
valuesClauses.push(`(${placeholders.join(', ')})`);
|
|
148
|
+
}
|
|
149
|
+
const sql = `INSERT INTO ${quotedTable} (${quotedColumns}) VALUES ${valuesClauses.join(', ')}`;
|
|
150
|
+
await this.dialect.query(sql, { raw: true, replacements: allParams });
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Execute a single record insert
|
|
154
|
+
*/
|
|
155
|
+
async executeSingleInsert(tableName, record, options) {
|
|
156
|
+
const { sql, values } = this.dialect.buildInsertQuery(tableName, record);
|
|
157
|
+
await this.dialect.query(sql, { raw: true, replacements: values });
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Optimize batch update
|
|
161
|
+
* @param tableName - Name of the table to update
|
|
162
|
+
* @param records - Records to update (must have primary key or unique identifier)
|
|
163
|
+
* @param identifierColumn - Column to use for identifying records
|
|
164
|
+
* @param options - Batch options
|
|
165
|
+
*/
|
|
166
|
+
async optimizeBatchUpdate(tableName, records, identifierColumn = 'id', options) {
|
|
167
|
+
const opts = { ...this.defaultOptions, ...options };
|
|
168
|
+
const startTime = Date.now();
|
|
169
|
+
if (records.length === 0) {
|
|
170
|
+
return {
|
|
171
|
+
success: [],
|
|
172
|
+
failed: [],
|
|
173
|
+
total: 0,
|
|
174
|
+
chunkSize: opts.chunkSize,
|
|
175
|
+
batches: 0,
|
|
176
|
+
duration: 0,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const success = [];
|
|
180
|
+
const failed = [];
|
|
181
|
+
const chunks = this.chunkArray(records, opts.chunkSize);
|
|
182
|
+
const totalBatches = Math.ceil(records.length / opts.chunkSize);
|
|
183
|
+
for (let batchIndex = 0; batchIndex < chunks.length; batchIndex++) {
|
|
184
|
+
const chunk = chunks[batchIndex];
|
|
185
|
+
try {
|
|
186
|
+
// Try batch UPDATE with CASE expressions (more efficient)
|
|
187
|
+
await this.executeBatchUpdate(tableName, chunk, identifierColumn, opts);
|
|
188
|
+
success.push(...chunk);
|
|
189
|
+
this.logProgress(opts, {
|
|
190
|
+
total: records.length,
|
|
191
|
+
processed: success.length + failed.length,
|
|
192
|
+
success: success.length,
|
|
193
|
+
failed: failed.length,
|
|
194
|
+
batchIndex,
|
|
195
|
+
totalBatches,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
if (opts.stopOnError) {
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
// Fall back to individual updates
|
|
203
|
+
for (const record of chunk) {
|
|
204
|
+
try {
|
|
205
|
+
const idValue = record[identifierColumn];
|
|
206
|
+
const updateValues = { ...record };
|
|
207
|
+
delete updateValues[identifierColumn];
|
|
208
|
+
await this.executeSingleUpdate(tableName, updateValues, { [identifierColumn]: idValue }, opts);
|
|
209
|
+
success.push(record);
|
|
210
|
+
}
|
|
211
|
+
catch (individualError) {
|
|
212
|
+
failed.push({ record, error: individualError });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
this.logProgress(opts, {
|
|
216
|
+
total: records.length,
|
|
217
|
+
processed: success.length + failed.length,
|
|
218
|
+
success: success.length,
|
|
219
|
+
failed: failed.length,
|
|
220
|
+
batchIndex,
|
|
221
|
+
totalBatches,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
success,
|
|
227
|
+
failed,
|
|
228
|
+
total: records.length,
|
|
229
|
+
chunkSize: opts.chunkSize,
|
|
230
|
+
batches: totalBatches,
|
|
231
|
+
duration: Date.now() - startTime,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Execute batch update using CASE expressions
|
|
236
|
+
* Uses bound parameters with dialect-correct placeholders instead of
|
|
237
|
+
* interpolating escaped literals directly into the SQL string.
|
|
238
|
+
*/
|
|
239
|
+
async executeBatchUpdate(tableName, records, identifierColumn, options) {
|
|
240
|
+
if (records.length === 0)
|
|
241
|
+
return;
|
|
242
|
+
const columns = Object.keys(records[0]).filter((col) => col !== identifierColumn);
|
|
243
|
+
const quotedTable = this.dialect.quoteTable(tableName);
|
|
244
|
+
// Build CASE expressions for each column
|
|
245
|
+
const caseClauses = [];
|
|
246
|
+
const params = [];
|
|
247
|
+
let paramIndex = 1;
|
|
248
|
+
for (const column of columns) {
|
|
249
|
+
const whenClauses = [];
|
|
250
|
+
for (const record of records) {
|
|
251
|
+
const idPlaceholder = this.nextPlaceholder(paramIndex++);
|
|
252
|
+
params.push(record[identifierColumn]);
|
|
253
|
+
const valuePlaceholder = this.nextPlaceholder(paramIndex++);
|
|
254
|
+
params.push(record[column]);
|
|
255
|
+
whenClauses.push(`WHEN ${idPlaceholder} THEN ${valuePlaceholder}`);
|
|
256
|
+
}
|
|
257
|
+
caseClauses.push(`${this.dialect.quoteIdentifier(column)} = CASE ${whenClauses.join(' ')} END`);
|
|
258
|
+
}
|
|
259
|
+
// Get unique IDs for WHERE clause
|
|
260
|
+
const ids = records.map((r) => r[identifierColumn]);
|
|
261
|
+
const idPlaceholders = ids.map(() => this.nextPlaceholder(paramIndex++));
|
|
262
|
+
params.push(...ids);
|
|
263
|
+
const whereClause = `WHERE ${this.dialect.quoteIdentifier(identifierColumn)} IN (${idPlaceholders.join(', ')})`;
|
|
264
|
+
const sql = `UPDATE ${quotedTable} SET ${caseClauses.join(', ')} ${whereClause}`;
|
|
265
|
+
await this.dialect.query(sql, { raw: true, replacements: params });
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Execute a single record update
|
|
269
|
+
*/
|
|
270
|
+
async executeSingleUpdate(tableName, values, where, options) {
|
|
271
|
+
const { sql, values: queryValues } = this.dialect.buildUpdateQuery(tableName, values, where);
|
|
272
|
+
await this.dialect.query(sql, { raw: true, replacements: queryValues });
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Optimize batch delete
|
|
276
|
+
* @param tableName - Name of the table to delete from
|
|
277
|
+
* @param identifiers - Array of identifiers for records to delete
|
|
278
|
+
* @param identifierColumn - Column to use for identifying records
|
|
279
|
+
* @param options - Batch options
|
|
280
|
+
*/
|
|
281
|
+
async optimizeBatchDelete(tableName, identifiers, identifierColumn = 'id', options) {
|
|
282
|
+
const opts = { ...this.defaultOptions, ...options };
|
|
283
|
+
const startTime = Date.now();
|
|
284
|
+
if (identifiers.length === 0) {
|
|
285
|
+
return {
|
|
286
|
+
success: [],
|
|
287
|
+
failed: [],
|
|
288
|
+
total: 0,
|
|
289
|
+
chunkSize: opts.chunkSize,
|
|
290
|
+
batches: 0,
|
|
291
|
+
duration: 0,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const success = [];
|
|
295
|
+
const failed = [];
|
|
296
|
+
const chunks = this.chunkArray(identifiers, opts.chunkSize);
|
|
297
|
+
const totalBatches = Math.ceil(identifiers.length / opts.chunkSize);
|
|
298
|
+
for (let batchIndex = 0; batchIndex < chunks.length; batchIndex++) {
|
|
299
|
+
const chunk = chunks[batchIndex];
|
|
300
|
+
try {
|
|
301
|
+
await this.executeBatchDelete(tableName, chunk, identifierColumn);
|
|
302
|
+
success.push(...chunk);
|
|
303
|
+
this.logProgress(opts, {
|
|
304
|
+
total: identifiers.length,
|
|
305
|
+
processed: success.length + failed.length,
|
|
306
|
+
success: success.length,
|
|
307
|
+
failed: failed.length,
|
|
308
|
+
batchIndex,
|
|
309
|
+
totalBatches,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
if (opts.stopOnError) {
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
// Fall back to individual deletes
|
|
317
|
+
for (const id of chunk) {
|
|
318
|
+
try {
|
|
319
|
+
await this.executeSingleDelete(tableName, { [identifierColumn]: id });
|
|
320
|
+
success.push(id);
|
|
321
|
+
}
|
|
322
|
+
catch (individualError) {
|
|
323
|
+
failed.push({ record: id, error: individualError });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
this.logProgress(opts, {
|
|
327
|
+
total: identifiers.length,
|
|
328
|
+
processed: success.length + failed.length,
|
|
329
|
+
success: success.length,
|
|
330
|
+
failed: failed.length,
|
|
331
|
+
batchIndex,
|
|
332
|
+
totalBatches,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
success,
|
|
338
|
+
failed,
|
|
339
|
+
total: identifiers.length,
|
|
340
|
+
chunkSize: opts.chunkSize,
|
|
341
|
+
batches: totalBatches,
|
|
342
|
+
duration: Date.now() - startTime,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Execute batch delete using IN clause
|
|
347
|
+
* Uses bound parameters with dialect-correct placeholders instead of
|
|
348
|
+
* interpolating escaped literals directly into the SQL string.
|
|
349
|
+
*/
|
|
350
|
+
async executeBatchDelete(tableName, identifiers, identifierColumn) {
|
|
351
|
+
if (identifiers.length === 0)
|
|
352
|
+
return;
|
|
353
|
+
const quotedTable = this.dialect.quoteTable(tableName);
|
|
354
|
+
let paramIndex = 1;
|
|
355
|
+
const placeholders = identifiers.map(() => this.nextPlaceholder(paramIndex++));
|
|
356
|
+
const whereClause = `WHERE ${this.dialect.quoteIdentifier(identifierColumn)} IN (${placeholders.join(', ')})`;
|
|
357
|
+
const sql = `DELETE FROM ${quotedTable} ${whereClause}`;
|
|
358
|
+
await this.dialect.query(sql, { raw: true, replacements: identifiers });
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Execute a single record delete
|
|
362
|
+
*/
|
|
363
|
+
async executeSingleDelete(tableName, where) {
|
|
364
|
+
const { sql, values } = this.dialect.buildDeleteQuery(tableName, where);
|
|
365
|
+
await this.dialect.query(sql, { raw: true, replacements: values });
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Check if error is a duplicate key error
|
|
369
|
+
*/
|
|
370
|
+
isDuplicateKeyError(error) {
|
|
371
|
+
const errorMessage = error?.message?.toLowerCase() || '';
|
|
372
|
+
return (errorMessage.includes('duplicate') ||
|
|
373
|
+
errorMessage.includes('unique constraint') ||
|
|
374
|
+
errorMessage.includes('unique key'));
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Chunk an array into smaller arrays
|
|
378
|
+
*/
|
|
379
|
+
chunkArray(array, size) {
|
|
380
|
+
const chunks = [];
|
|
381
|
+
for (let i = 0; i < array.length; i += size) {
|
|
382
|
+
chunks.push(array.slice(i, i + size));
|
|
383
|
+
}
|
|
384
|
+
return chunks;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Log progress
|
|
388
|
+
*/
|
|
389
|
+
logProgress(options, progress) {
|
|
390
|
+
if (options.logging) {
|
|
391
|
+
const percent = Math.round((progress.processed / progress.total) * 100);
|
|
392
|
+
options.logging(`Batch progress: ${progress.processed}/${progress.total} (${percent}%)`);
|
|
393
|
+
}
|
|
394
|
+
if (options.onProgress) {
|
|
395
|
+
options.onProgress(progress);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
exports.BatchQueryOptimizer = BatchQueryOptimizer;
|
|
400
|
+
/**
|
|
401
|
+
* Optimize a batch insert operation
|
|
402
|
+
*/
|
|
403
|
+
async function optimizeBatchInsert(dialect, tableName, records, options) {
|
|
404
|
+
const optimizer = new BatchQueryOptimizer(dialect, options);
|
|
405
|
+
return optimizer.optimizeBatchInsert(tableName, records, options);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Optimize a batch update operation
|
|
409
|
+
*/
|
|
410
|
+
async function optimizeBatchUpdate(dialect, tableName, records, identifierColumn, options) {
|
|
411
|
+
const optimizer = new BatchQueryOptimizer(dialect, options);
|
|
412
|
+
return optimizer.optimizeBatchUpdate(tableName, records, identifierColumn, options);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Optimize a batch delete operation
|
|
416
|
+
*/
|
|
417
|
+
async function optimizeBatchDelete(dialect, tableName, identifiers, identifierColumn, options) {
|
|
418
|
+
const optimizer = new BatchQueryOptimizer(dialect, options);
|
|
419
|
+
return optimizer.optimizeBatchDelete(tableName, identifiers, identifierColumn, options);
|
|
420
|
+
}
|
|
421
|
+
exports.default = {
|
|
422
|
+
BatchQueryOptimizer,
|
|
423
|
+
optimizeBatchInsert,
|
|
424
|
+
optimizeBatchUpdate,
|
|
425
|
+
optimizeBatchDelete,
|
|
426
|
+
};
|