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,278 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Query Optimizer - Main entry point that combines all optimization features
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.QueryOptimizer = void 0;
|
|
7
|
+
exports.createQueryOptimizer = createQueryOptimizer;
|
|
8
|
+
const query_hints_1 = require("./query-hints");
|
|
9
|
+
const explain_plans_1 = require("./explain-plans");
|
|
10
|
+
const slow_query_logger_1 = require("./slow-query-logger");
|
|
11
|
+
const batch_optimizer_1 = require("./batch-optimizer");
|
|
12
|
+
const prepared_statement_cache_1 = require("./prepared-statement-cache");
|
|
13
|
+
/**
|
|
14
|
+
* Main query optimizer class that combines all optimization features
|
|
15
|
+
*/
|
|
16
|
+
class QueryOptimizer {
|
|
17
|
+
constructor(dialect, options) {
|
|
18
|
+
this.dialect = dialect;
|
|
19
|
+
this.options = {
|
|
20
|
+
enableHints: options?.enableHints ?? true,
|
|
21
|
+
enableExplain: options?.enableExplain ?? process.env.NODE_ENV === 'development',
|
|
22
|
+
enableSlowQueryLog: options?.enableSlowQueryLog ?? true,
|
|
23
|
+
enablePreparedStatementCache: options?.enablePreparedStatementCache ?? true,
|
|
24
|
+
enableQueryResultCache: options?.enableQueryResultCache ?? true,
|
|
25
|
+
enableBatchOptimization: options?.enableBatchOptimization ?? true,
|
|
26
|
+
slowQueryOptions: {
|
|
27
|
+
threshold: 1000,
|
|
28
|
+
...options?.slowQueryOptions,
|
|
29
|
+
},
|
|
30
|
+
preparedStatementCacheOptions: {
|
|
31
|
+
maxSize: 500,
|
|
32
|
+
...options?.preparedStatementCacheOptions,
|
|
33
|
+
},
|
|
34
|
+
queryResultCacheOptions: {
|
|
35
|
+
maxSize: 100,
|
|
36
|
+
ttl: 60000,
|
|
37
|
+
...options?.queryResultCacheOptions,
|
|
38
|
+
},
|
|
39
|
+
batchOptions: {
|
|
40
|
+
chunkSize: 1000,
|
|
41
|
+
useTransaction: true,
|
|
42
|
+
...options?.batchOptions,
|
|
43
|
+
},
|
|
44
|
+
explainOptions: {
|
|
45
|
+
...options?.explainOptions,
|
|
46
|
+
},
|
|
47
|
+
logging: options?.logging,
|
|
48
|
+
};
|
|
49
|
+
this.initializeComponents();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Initialize all optimization components
|
|
53
|
+
*/
|
|
54
|
+
initializeComponents() {
|
|
55
|
+
if (this.options.enableHints) {
|
|
56
|
+
this.queryHintManager = new query_hints_1.QueryHintManager(this.dialect);
|
|
57
|
+
}
|
|
58
|
+
if (this.options.enableExplain) {
|
|
59
|
+
this.explainPlanGenerator = new explain_plans_1.ExplainPlanGenerator(this.dialect, this.options.explainOptions);
|
|
60
|
+
}
|
|
61
|
+
if (this.options.enableSlowQueryLog) {
|
|
62
|
+
this.slowQueryLogger = new slow_query_logger_1.SlowQueryLogger(this.options.slowQueryOptions);
|
|
63
|
+
}
|
|
64
|
+
if (this.options.enablePreparedStatementCache) {
|
|
65
|
+
this.preparedStatementCache = new prepared_statement_cache_1.PreparedStatementCache(this.dialect, this.options.preparedStatementCacheOptions);
|
|
66
|
+
}
|
|
67
|
+
if (this.options.enableQueryResultCache) {
|
|
68
|
+
this.queryResultCache = new prepared_statement_cache_1.QueryResultCache(this.options.queryResultCacheOptions);
|
|
69
|
+
}
|
|
70
|
+
if (this.options.enableBatchOptimization) {
|
|
71
|
+
this.batchOptimizer = new batch_optimizer_1.BatchQueryOptimizer(this.dialect, this.options.batchOptions);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the query hint manager
|
|
76
|
+
*/
|
|
77
|
+
getHintManager() {
|
|
78
|
+
return this.queryHintManager;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the explain plan generator
|
|
82
|
+
*/
|
|
83
|
+
getExplainGenerator() {
|
|
84
|
+
return this.explainPlanGenerator;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the slow query logger
|
|
88
|
+
*/
|
|
89
|
+
getSlowQueryLogger() {
|
|
90
|
+
return this.slowQueryLogger;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the prepared statement cache
|
|
94
|
+
*/
|
|
95
|
+
getPreparedStatementCache() {
|
|
96
|
+
return this.preparedStatementCache;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get the query result cache
|
|
100
|
+
*/
|
|
101
|
+
getQueryResultCache() {
|
|
102
|
+
return this.queryResultCache;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get the batch optimizer
|
|
106
|
+
*/
|
|
107
|
+
getBatchOptimizer() {
|
|
108
|
+
return this.batchOptimizer;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Apply query hints to a SQL statement
|
|
112
|
+
*/
|
|
113
|
+
applyHints(sql, hints) {
|
|
114
|
+
if (!this.queryHintManager) {
|
|
115
|
+
return sql;
|
|
116
|
+
}
|
|
117
|
+
return this.queryHintManager.applyHints(sql, hints);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Execute an optimized query with caching support
|
|
121
|
+
*/
|
|
122
|
+
async executeQuery(sql, options) {
|
|
123
|
+
const startTime = Date.now();
|
|
124
|
+
// Apply hints if provided
|
|
125
|
+
let finalSql = sql;
|
|
126
|
+
if (options?.hint && this.queryHintManager) {
|
|
127
|
+
finalSql = this.queryHintManager.applyHints(sql, options.hint);
|
|
128
|
+
}
|
|
129
|
+
// Check query result cache
|
|
130
|
+
if (options?.useCache && this.queryResultCache && !options.params) {
|
|
131
|
+
const cached = this.queryResultCache.get(finalSql);
|
|
132
|
+
if (cached !== undefined) {
|
|
133
|
+
return { result: cached, fromCache: true, duration: Date.now() - startTime };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Check prepared statement cache
|
|
137
|
+
let cacheKey;
|
|
138
|
+
if (options?.usePreparedStatement && this.preparedStatementCache) {
|
|
139
|
+
const cached = this.preparedStatementCache.get(finalSql, options.params);
|
|
140
|
+
if (cached) {
|
|
141
|
+
// Use cached prepared statement
|
|
142
|
+
const execStart = Date.now();
|
|
143
|
+
const result = await this.dialect.query(finalSql, { raw: true });
|
|
144
|
+
this.preparedStatementCache.recordExecution(cached.key, Date.now() - execStart);
|
|
145
|
+
// Log slow query
|
|
146
|
+
if (!options?.skipSlowLog && this.slowQueryLogger) {
|
|
147
|
+
this.slowQueryLogger.logQuery({
|
|
148
|
+
sql: finalSql,
|
|
149
|
+
params: options.params,
|
|
150
|
+
duration: Date.now() - startTime,
|
|
151
|
+
dialect: this.dialect.name,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return { result, fromCache: false, duration: Date.now() - startTime };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Execute query
|
|
158
|
+
const result = await this.dialect.query(finalSql, { raw: true });
|
|
159
|
+
const duration = Date.now() - startTime;
|
|
160
|
+
// Cache result if enabled
|
|
161
|
+
if (options?.useCache && this.queryResultCache) {
|
|
162
|
+
this.queryResultCache.set(finalSql, options.params ?? [], result);
|
|
163
|
+
}
|
|
164
|
+
// Track statement usage if enabled. Note: `PreparedStatementCache` does not
|
|
165
|
+
// hold a real driver-level prepared-statement handle (see its class doc) -
|
|
166
|
+
// `compiled` here is just a lightweight, genuinely reusable descriptor of
|
|
167
|
+
// the statement shape (SQL + bound parameter count).
|
|
168
|
+
if (options?.usePreparedStatement && this.preparedStatementCache) {
|
|
169
|
+
this.preparedStatementCache.set(finalSql, { sql: finalSql, paramCount: options.params?.length ?? 0 }, options.params);
|
|
170
|
+
}
|
|
171
|
+
// Log slow query
|
|
172
|
+
if (!options?.skipSlowLog && this.slowQueryLogger) {
|
|
173
|
+
this.slowQueryLogger.logQuery({
|
|
174
|
+
sql: finalSql,
|
|
175
|
+
params: options?.params,
|
|
176
|
+
duration,
|
|
177
|
+
dialect: this.dialect.name,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return { result, fromCache: false, duration };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Explain a query
|
|
184
|
+
*/
|
|
185
|
+
async explainQuery(sql, options) {
|
|
186
|
+
if (!this.explainPlanGenerator) {
|
|
187
|
+
throw new Error('Explain plans are not enabled');
|
|
188
|
+
}
|
|
189
|
+
return this.explainPlanGenerator.explain(sql, options);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Analyze a query for optimization opportunities
|
|
193
|
+
*/
|
|
194
|
+
async analyzeQuery(sql, options) {
|
|
195
|
+
if (!this.explainPlanGenerator) {
|
|
196
|
+
throw new Error('Explain plans are not enabled');
|
|
197
|
+
}
|
|
198
|
+
return this.explainPlanGenerator.analyzeQuery(sql, options);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Perform batch insert with optimization
|
|
202
|
+
*/
|
|
203
|
+
async batchInsert(tableName, records, options) {
|
|
204
|
+
if (!this.batchOptimizer) {
|
|
205
|
+
throw new Error('Batch optimization is not enabled');
|
|
206
|
+
}
|
|
207
|
+
return this.batchOptimizer.optimizeBatchInsert(tableName, records, options);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Perform batch update with optimization
|
|
211
|
+
*/
|
|
212
|
+
async batchUpdate(tableName, records, identifierColumn, options) {
|
|
213
|
+
if (!this.batchOptimizer) {
|
|
214
|
+
throw new Error('Batch optimization is not enabled');
|
|
215
|
+
}
|
|
216
|
+
return this.batchOptimizer.optimizeBatchUpdate(tableName, records, identifierColumn, options);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Perform batch delete with optimization
|
|
220
|
+
*/
|
|
221
|
+
async batchDelete(tableName, identifiers, identifierColumn, options) {
|
|
222
|
+
if (!this.batchOptimizer) {
|
|
223
|
+
throw new Error('Batch optimization is not enabled');
|
|
224
|
+
}
|
|
225
|
+
return this.batchOptimizer.optimizeBatchDelete(tableName, identifiers, identifierColumn, options);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get optimization statistics
|
|
229
|
+
*/
|
|
230
|
+
getStats() {
|
|
231
|
+
return {
|
|
232
|
+
slowQuery: this.slowQueryLogger?.getStatistics(),
|
|
233
|
+
preparedStatementCache: this.preparedStatementCache?.getStats(),
|
|
234
|
+
queryResultCache: this.queryResultCache?.getStats(),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Clear all caches
|
|
239
|
+
*/
|
|
240
|
+
clearCaches() {
|
|
241
|
+
this.preparedStatementCache?.clear();
|
|
242
|
+
this.queryResultCache?.clear();
|
|
243
|
+
this.slowQueryLogger?.clear();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Prune expired cache entries
|
|
247
|
+
*/
|
|
248
|
+
pruneCaches() {
|
|
249
|
+
this.preparedStatementCache?.prune();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Invalidate query result cache for a specific table
|
|
253
|
+
*/
|
|
254
|
+
invalidateTableCache(tableName) {
|
|
255
|
+
const resultCacheInvalidated = this.queryResultCache?.invalidateTable(tableName) ?? 0;
|
|
256
|
+
const preparedStatementCacheInvalidated = this.preparedStatementCache?.invalidateTable(tableName) ?? 0;
|
|
257
|
+
return resultCacheInvalidated + preparedStatementCacheInvalidated;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Log a message
|
|
261
|
+
*/
|
|
262
|
+
log(message, level = 'info') {
|
|
263
|
+
if (this.options.logging) {
|
|
264
|
+
this.options.logging(message, level);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
exports.QueryOptimizer = QueryOptimizer;
|
|
269
|
+
/**
|
|
270
|
+
* Create a query optimizer instance
|
|
271
|
+
*/
|
|
272
|
+
function createQueryOptimizer(dialect, options) {
|
|
273
|
+
return new QueryOptimizer(dialect, options);
|
|
274
|
+
}
|
|
275
|
+
exports.default = {
|
|
276
|
+
QueryOptimizer,
|
|
277
|
+
createQueryOptimizer,
|
|
278
|
+
};
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Slow Query Logging
|
|
4
|
+
* Provides functionality to log and analyze slow database queries
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SlowQueryLogger = void 0;
|
|
8
|
+
exports.createSlowQueryLogger = createSlowQueryLogger;
|
|
9
|
+
const events_1 = require("events");
|
|
10
|
+
/**
|
|
11
|
+
* Slow query logger
|
|
12
|
+
*/
|
|
13
|
+
class SlowQueryLogger extends events_1.EventEmitter {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
super();
|
|
16
|
+
this.logAllQueries = false;
|
|
17
|
+
this.logParams = false;
|
|
18
|
+
this.maxEntries = 1000;
|
|
19
|
+
this.includeStackTrace = false;
|
|
20
|
+
this.logs = [];
|
|
21
|
+
this.alertThreshold = 10;
|
|
22
|
+
this.alertWindow = 60000; // 1 minute
|
|
23
|
+
this.queryTimestamps = [];
|
|
24
|
+
this.threshold = options.threshold || SlowQueryLogger.DEFAULT_THRESHOLD;
|
|
25
|
+
this.logAllQueries = options.logAllQueries || false;
|
|
26
|
+
this.logParams = options.logParams || false;
|
|
27
|
+
this.maxEntries = options.maxEntries || 1000;
|
|
28
|
+
this.onSlowQuery = options.onSlowQuery;
|
|
29
|
+
this.onAlert = options.onAlert;
|
|
30
|
+
this.includeStackTrace = options.includeStackTrace || false;
|
|
31
|
+
this.logger = options.logger;
|
|
32
|
+
if (options.alertThreshold !== undefined) {
|
|
33
|
+
this.alertThreshold = options.alertThreshold;
|
|
34
|
+
}
|
|
35
|
+
if (options.alertWindow !== undefined) {
|
|
36
|
+
this.alertWindow = options.alertWindow;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Set the slow query threshold
|
|
41
|
+
*/
|
|
42
|
+
setThreshold(threshold) {
|
|
43
|
+
this.threshold = threshold;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the current threshold
|
|
47
|
+
*/
|
|
48
|
+
getThreshold() {
|
|
49
|
+
return this.threshold;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Enable logging of all queries (not just slow ones)
|
|
53
|
+
*/
|
|
54
|
+
setLogAllQueries(logAll) {
|
|
55
|
+
this.logAllQueries = logAll;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Log a query execution
|
|
59
|
+
*/
|
|
60
|
+
logQuery(options) {
|
|
61
|
+
const entry = {
|
|
62
|
+
id: this.generateId(),
|
|
63
|
+
sql: options.sql,
|
|
64
|
+
params: this.logParams ? options.params : undefined,
|
|
65
|
+
duration: options.duration,
|
|
66
|
+
timestamp: new Date(),
|
|
67
|
+
dialect: options.dialect,
|
|
68
|
+
inTransaction: options.inTransaction || false,
|
|
69
|
+
connection: options.connection,
|
|
70
|
+
error: options.error,
|
|
71
|
+
queryType: options.queryType,
|
|
72
|
+
rowCount: options.rowCount,
|
|
73
|
+
};
|
|
74
|
+
if (this.includeStackTrace) {
|
|
75
|
+
entry.stackTrace = new Error().stack;
|
|
76
|
+
}
|
|
77
|
+
// Determine if this should be logged
|
|
78
|
+
const shouldLog = this.logAllQueries || options.duration >= this.threshold;
|
|
79
|
+
if (shouldLog) {
|
|
80
|
+
this.addLogEntry(entry);
|
|
81
|
+
// Log the query
|
|
82
|
+
this.logEntry(entry);
|
|
83
|
+
// Emit events
|
|
84
|
+
this.emit('query', entry);
|
|
85
|
+
if (options.duration >= this.threshold) {
|
|
86
|
+
this.emit('slowQuery', entry);
|
|
87
|
+
if (this.onSlowQuery) {
|
|
88
|
+
this.onSlowQuery(entry);
|
|
89
|
+
}
|
|
90
|
+
// Check alert threshold
|
|
91
|
+
this.checkAlertThreshold();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return shouldLog ? entry : null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Add a log entry to the internal log storage
|
|
98
|
+
*/
|
|
99
|
+
addLogEntry(entry) {
|
|
100
|
+
this.logs.push(entry);
|
|
101
|
+
// Maintain max entries limit
|
|
102
|
+
if (this.logs.length > this.maxEntries) {
|
|
103
|
+
this.logs.shift();
|
|
104
|
+
}
|
|
105
|
+
// Track query timestamps for alerting
|
|
106
|
+
if (entry.duration >= this.threshold) {
|
|
107
|
+
this.queryTimestamps.push(Date.now());
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Log the entry using the configured logger
|
|
112
|
+
*/
|
|
113
|
+
logEntry(entry) {
|
|
114
|
+
const isSlow = entry.duration >= this.threshold;
|
|
115
|
+
const level = isSlow ? 'warn' : 'debug';
|
|
116
|
+
const meta = {
|
|
117
|
+
duration: `${entry.duration}ms`,
|
|
118
|
+
dialect: entry.dialect,
|
|
119
|
+
queryType: entry.queryType,
|
|
120
|
+
rowCount: entry.rowCount,
|
|
121
|
+
inTransaction: entry.inTransaction,
|
|
122
|
+
};
|
|
123
|
+
const message = isSlow
|
|
124
|
+
? `[SLOW] Query exceeded threshold (${this.threshold}ms): ${entry.sql}`
|
|
125
|
+
: entry.sql;
|
|
126
|
+
if (this.logger) {
|
|
127
|
+
this.logger(message, meta);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Default console logging
|
|
131
|
+
if (isSlow) {
|
|
132
|
+
console.warn(`[SLOW QUERY] ${message}`, meta);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
console.log(`[QUERY] ${message}`, meta);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check if alert threshold has been exceeded
|
|
141
|
+
*/
|
|
142
|
+
checkAlertThreshold() {
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
const windowStart = now - this.alertWindow;
|
|
145
|
+
// Remove timestamps outside the window
|
|
146
|
+
this.queryTimestamps = this.queryTimestamps.filter((ts) => ts >= windowStart);
|
|
147
|
+
if (this.queryTimestamps.length >= this.alertThreshold) {
|
|
148
|
+
// Get all slow queries in the window
|
|
149
|
+
const alertEntries = this.logs.filter((entry) => entry.timestamp.getTime() >= windowStart && entry.duration >= this.threshold);
|
|
150
|
+
this.emit('alert', alertEntries);
|
|
151
|
+
if (this.onAlert) {
|
|
152
|
+
this.onAlert(alertEntries);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get all log entries
|
|
158
|
+
*/
|
|
159
|
+
getLogs() {
|
|
160
|
+
return [...this.logs];
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get slow query entries only
|
|
164
|
+
*/
|
|
165
|
+
getSlowQueries() {
|
|
166
|
+
return this.logs.filter((entry) => entry.duration >= this.threshold);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get query statistics
|
|
170
|
+
*/
|
|
171
|
+
getStatistics() {
|
|
172
|
+
const slowQueries = this.getSlowQueries();
|
|
173
|
+
const totalQueries = this.logs.length;
|
|
174
|
+
if (totalQueries === 0) {
|
|
175
|
+
return {
|
|
176
|
+
totalQueries: 0,
|
|
177
|
+
slowQueries: 0,
|
|
178
|
+
averageDuration: 0,
|
|
179
|
+
maxDuration: 0,
|
|
180
|
+
minDuration: 0,
|
|
181
|
+
p50Duration: 0,
|
|
182
|
+
p95Duration: 0,
|
|
183
|
+
p99Duration: 0,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const durations = this.logs.map((e) => e.duration).sort((a, b) => a - b);
|
|
187
|
+
return {
|
|
188
|
+
totalQueries,
|
|
189
|
+
slowQueries: slowQueries.length,
|
|
190
|
+
averageDuration: durations.reduce((a, b) => a + b, 0) / totalQueries,
|
|
191
|
+
maxDuration: durations[durations.length - 1],
|
|
192
|
+
minDuration: durations[0],
|
|
193
|
+
p50Duration: this.percentile(durations, 50),
|
|
194
|
+
p95Duration: this.percentile(durations, 95),
|
|
195
|
+
p99Duration: this.percentile(durations, 99),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Calculate percentile from sorted array
|
|
200
|
+
*/
|
|
201
|
+
percentile(sortedArray, p) {
|
|
202
|
+
if (sortedArray.length === 0)
|
|
203
|
+
return 0;
|
|
204
|
+
const index = Math.ceil((p / 100) * sortedArray.length) - 1;
|
|
205
|
+
return sortedArray[Math.max(0, index)];
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Clear all log entries
|
|
209
|
+
*/
|
|
210
|
+
clear() {
|
|
211
|
+
this.logs = [];
|
|
212
|
+
this.queryTimestamps = [];
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Generate a unique ID for log entries
|
|
216
|
+
*/
|
|
217
|
+
generateId() {
|
|
218
|
+
return `sq_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Export logs to a file-compatible format
|
|
222
|
+
*/
|
|
223
|
+
exportLogs() {
|
|
224
|
+
return JSON.stringify(this.logs, null, 2);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get queries by type
|
|
228
|
+
*/
|
|
229
|
+
getQueriesByType(type) {
|
|
230
|
+
return this.logs.filter((entry) => entry.queryType?.toUpperCase() === type.toUpperCase());
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get queries on a specific table
|
|
234
|
+
*/
|
|
235
|
+
getQueriesOnTable(tableName) {
|
|
236
|
+
const regex = new RegExp(`\\b${tableName}\\b`, 'i');
|
|
237
|
+
return this.logs.filter((entry) => regex.test(entry.sql));
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Find similar queries (group by SQL pattern)
|
|
241
|
+
*/
|
|
242
|
+
findSimilarQueries(sql) {
|
|
243
|
+
// Normalize SQL by replacing literal values with placeholders
|
|
244
|
+
const normalizedSql = this.normalizeSql(sql);
|
|
245
|
+
const normalized = this.normalizeSql(normalizedSql);
|
|
246
|
+
return this.logs.filter((entry) => this.normalizeSql(entry.sql) === normalized);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Normalize SQL for comparison
|
|
250
|
+
*/
|
|
251
|
+
normalizeSql(sql) {
|
|
252
|
+
return sql
|
|
253
|
+
.replace(/'[^']*'/g, '?')
|
|
254
|
+
.replace(/"[^"]*"/g, '?')
|
|
255
|
+
.replace(/\d+/g, '?')
|
|
256
|
+
.replace(/\s+/g, ' ')
|
|
257
|
+
.trim();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.SlowQueryLogger = SlowQueryLogger;
|
|
261
|
+
SlowQueryLogger.DEFAULT_THRESHOLD = 1000; // 1 second
|
|
262
|
+
/**
|
|
263
|
+
* Create a slow query logger instance
|
|
264
|
+
*/
|
|
265
|
+
function createSlowQueryLogger(options) {
|
|
266
|
+
return new SlowQueryLogger(options);
|
|
267
|
+
}
|
|
268
|
+
exports.default = {
|
|
269
|
+
SlowQueryLogger,
|
|
270
|
+
createSlowQueryLogger,
|
|
271
|
+
};
|