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,423 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prepared Statement Caching
|
|
4
|
+
* Provides functionality to cache and reuse prepared statements for better performance
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.QueryResultCache = exports.PreparedStatementCache = void 0;
|
|
8
|
+
exports.createPreparedStatementCache = createPreparedStatementCache;
|
|
9
|
+
/**
|
|
10
|
+
* Tracks SQL statement usage (hit/miss counts, execution timings, last-used
|
|
11
|
+
* time) keyed by normalized SQL + params, with TTL-based expiry.
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANT — naming caveat: despite the name, this does **not** cache a real
|
|
14
|
+
* driver-level prepared-statement handle. None of the `Dialect` implementations
|
|
15
|
+
* in `src/dialects/**` expose a separate prepare/execute split - `query(sql,
|
|
16
|
+
* options)` always receives the full SQL text and applies `options.replacements`
|
|
17
|
+
* / `options.bind` per call (see e.g. `sqlite/index.ts` `query()`, which calls
|
|
18
|
+
* `connection.prepare(sql)` fresh on every invocation). So there is no reusable
|
|
19
|
+
* driver object for this class to hold onto; the `compiled` field on each entry
|
|
20
|
+
* is whatever metadata the caller passes to `set()` (by default just `{}`, or -
|
|
21
|
+
* see `QueryOptimizer.executeQuery()` - a lightweight `{ sql, paramCount }`
|
|
22
|
+
* descriptor). In practice this class behaves as a query-usage/hit-count
|
|
23
|
+
* tracker with an optional metadata slot, not a real statement cache. It is
|
|
24
|
+
* kept under this name for backwards compatibility with existing callers/tests;
|
|
25
|
+
* treat `getStats()` / `hitCount` / `topStatements` as the reliable surface,
|
|
26
|
+
* and don't rely on `compiled` holding an executable handle.
|
|
27
|
+
*
|
|
28
|
+
* Invalidation: entries expire via TTL (`options.ttl`, default 1 hour). Since
|
|
29
|
+
* that alone can serve stale-shaped results after schema changes made via raw
|
|
30
|
+
* DDL, `invalidateTable()` is provided to drop entries referencing a given
|
|
31
|
+
* table name on write (e.g. from a caller's insert/update/delete path); it is
|
|
32
|
+
* not auto-wired to every write in this codebase, but `QueryOptimizer.
|
|
33
|
+
* invalidateTableCache()` calls it alongside the query-result cache.
|
|
34
|
+
*/
|
|
35
|
+
class PreparedStatementCache {
|
|
36
|
+
constructor(dialect, options) {
|
|
37
|
+
this.stats = {
|
|
38
|
+
hits: 0,
|
|
39
|
+
misses: 0,
|
|
40
|
+
evictions: 0,
|
|
41
|
+
totalExecutionTime: 0,
|
|
42
|
+
};
|
|
43
|
+
this.dialect = dialect;
|
|
44
|
+
this.cache = new Map();
|
|
45
|
+
this.maxSize = options.maxSize || 500;
|
|
46
|
+
this.ttl = options.ttl || 3600000; // 1 hour default
|
|
47
|
+
this.normalizeSql = options.normalizeSql !== false;
|
|
48
|
+
this.onCache = options.onCache;
|
|
49
|
+
this.onEvict = options.onEvict;
|
|
50
|
+
this.logging = options.logging || false;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a cached prepared statement
|
|
54
|
+
*/
|
|
55
|
+
get(sql, params) {
|
|
56
|
+
const key = this.generateKey(sql, params);
|
|
57
|
+
const entry = this.cache.get(key);
|
|
58
|
+
if (entry) {
|
|
59
|
+
// Check if entry has expired
|
|
60
|
+
if (Date.now() - entry.createdAt > this.ttl) {
|
|
61
|
+
this.evict(key, entry);
|
|
62
|
+
this.stats.misses++;
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
// Update statistics
|
|
66
|
+
entry.hitCount++;
|
|
67
|
+
entry.lastUsedAt = Date.now();
|
|
68
|
+
this.stats.hits++;
|
|
69
|
+
this.log(`Cache hit for: ${key} (hits: ${entry.hitCount})`);
|
|
70
|
+
return entry;
|
|
71
|
+
}
|
|
72
|
+
this.stats.misses++;
|
|
73
|
+
this.log(`Cache miss for: ${key}`);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Store a prepared statement in the cache
|
|
78
|
+
*/
|
|
79
|
+
set(sql, compiled, params) {
|
|
80
|
+
const key = this.generateKey(sql, params);
|
|
81
|
+
// Check if we need to evict entries
|
|
82
|
+
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
83
|
+
this.evictOldest();
|
|
84
|
+
}
|
|
85
|
+
const entry = {
|
|
86
|
+
key,
|
|
87
|
+
sql,
|
|
88
|
+
compiled,
|
|
89
|
+
createdAt: Date.now(),
|
|
90
|
+
hitCount: 0,
|
|
91
|
+
lastUsedAt: Date.now(),
|
|
92
|
+
executions: 0,
|
|
93
|
+
totalExecutionTime: 0,
|
|
94
|
+
};
|
|
95
|
+
this.cache.set(key, entry);
|
|
96
|
+
if (this.onCache) {
|
|
97
|
+
this.onCache(key, entry);
|
|
98
|
+
}
|
|
99
|
+
this.log(`Cached statement: ${key}`);
|
|
100
|
+
return entry;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Record execution of a prepared statement
|
|
104
|
+
*/
|
|
105
|
+
recordExecution(key, executionTime) {
|
|
106
|
+
const entry = this.cache.get(key);
|
|
107
|
+
if (entry) {
|
|
108
|
+
entry.executions++;
|
|
109
|
+
entry.totalExecutionTime += executionTime;
|
|
110
|
+
this.stats.totalExecutionTime += executionTime;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if a statement is cached
|
|
115
|
+
*/
|
|
116
|
+
has(sql, params) {
|
|
117
|
+
const key = this.generateKey(sql, params);
|
|
118
|
+
const entry = this.cache.get(key);
|
|
119
|
+
if (!entry)
|
|
120
|
+
return false;
|
|
121
|
+
// Check if entry has expired
|
|
122
|
+
if (Date.now() - entry.createdAt > this.ttl) {
|
|
123
|
+
this.evict(key, entry);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clear all cached statements
|
|
130
|
+
*/
|
|
131
|
+
clear() {
|
|
132
|
+
this.cache.clear();
|
|
133
|
+
this.stats = {
|
|
134
|
+
hits: 0,
|
|
135
|
+
misses: 0,
|
|
136
|
+
evictions: 0,
|
|
137
|
+
totalExecutionTime: 0,
|
|
138
|
+
};
|
|
139
|
+
this.log('Cache cleared');
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get cache statistics
|
|
143
|
+
*/
|
|
144
|
+
getStats() {
|
|
145
|
+
const total = this.stats.hits + this.stats.misses;
|
|
146
|
+
const hitRate = total > 0 ? (this.stats.hits / total) * 100 : 0;
|
|
147
|
+
// Get most used statements
|
|
148
|
+
const topStatements = Array.from(this.cache.values())
|
|
149
|
+
.sort((a, b) => b.hitCount - a.hitCount)
|
|
150
|
+
.slice(0, 10)
|
|
151
|
+
.map((s) => ({
|
|
152
|
+
sql: s.sql,
|
|
153
|
+
hits: s.hitCount,
|
|
154
|
+
executions: s.executions,
|
|
155
|
+
avgExecutionTime: s.executions > 0 ? s.totalExecutionTime / s.executions : 0,
|
|
156
|
+
}));
|
|
157
|
+
return {
|
|
158
|
+
size: this.cache.size,
|
|
159
|
+
maxSize: this.maxSize,
|
|
160
|
+
hits: this.stats.hits,
|
|
161
|
+
misses: this.stats.misses,
|
|
162
|
+
hitRate: Math.round(hitRate * 100) / 100,
|
|
163
|
+
totalExecutions: this.stats.totalExecutionTime,
|
|
164
|
+
evictions: this.stats.evictions,
|
|
165
|
+
topStatements,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get all cached statements
|
|
170
|
+
*/
|
|
171
|
+
getAll() {
|
|
172
|
+
return Array.from(this.cache.values());
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Generate a cache key from SQL and optional parameters
|
|
176
|
+
*/
|
|
177
|
+
generateKey(sql, params) {
|
|
178
|
+
let normalizedSql = sql;
|
|
179
|
+
if (this.normalizeSql) {
|
|
180
|
+
// Normalize SQL by replacing values with placeholders
|
|
181
|
+
normalizedSql = this.normalizeSQL(sql);
|
|
182
|
+
}
|
|
183
|
+
if (params && params.length > 0) {
|
|
184
|
+
// Include params in key if not normalizing SQL
|
|
185
|
+
return `${normalizedSql}::${JSON.stringify(params)}`;
|
|
186
|
+
}
|
|
187
|
+
return normalizedSql;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Normalize SQL for consistent caching
|
|
191
|
+
*/
|
|
192
|
+
normalizeSQL(sql) {
|
|
193
|
+
return (sql
|
|
194
|
+
// Remove extra whitespace
|
|
195
|
+
.replace(/\s+/g, ' ')
|
|
196
|
+
// Remove comments
|
|
197
|
+
.replace(/--.*$/gm, '')
|
|
198
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
199
|
+
// Uppercase keywords
|
|
200
|
+
.replace(/\b(SELECT|FROM|WHERE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|AND|OR|ORDER|BY|GROUP|HAVING|LIMIT|OFFSET|INSERT|INTO|VALUES|UPDATE|SET|DELETE)\b/gi, (match) => match.toUpperCase())
|
|
201
|
+
.trim());
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Evict the oldest entry from cache
|
|
205
|
+
*/
|
|
206
|
+
evictOldest() {
|
|
207
|
+
let oldestKey = null;
|
|
208
|
+
let oldestTime = Date.now();
|
|
209
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
210
|
+
if (entry.lastUsedAt < oldestTime) {
|
|
211
|
+
oldestTime = entry.lastUsedAt;
|
|
212
|
+
oldestKey = key;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (oldestKey) {
|
|
216
|
+
const entry = this.cache.get(oldestKey);
|
|
217
|
+
if (entry) {
|
|
218
|
+
this.evict(oldestKey, entry);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Evict a specific entry from cache
|
|
224
|
+
*/
|
|
225
|
+
evict(key, entry) {
|
|
226
|
+
this.cache.delete(key);
|
|
227
|
+
this.stats.evictions++;
|
|
228
|
+
if (this.onEvict) {
|
|
229
|
+
this.onEvict(key, entry);
|
|
230
|
+
}
|
|
231
|
+
this.log(`Evicted: ${key}`);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Log a message if logging is enabled
|
|
235
|
+
*/
|
|
236
|
+
log(message) {
|
|
237
|
+
if (this.logging) {
|
|
238
|
+
console.log(`[PreparedStatementCache] ${message}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Invalidate (remove) cache entries whose SQL references the given table.
|
|
243
|
+
* Intended to be called by write paths (INSERT/UPDATE/DELETE/DDL) so cached
|
|
244
|
+
* entries can't outlive schema/data changes for that table between TTL
|
|
245
|
+
* expirations. Not automatically wired into every write in this codebase -
|
|
246
|
+
* callers that write outside of `QueryOptimizer` should call this explicitly.
|
|
247
|
+
*/
|
|
248
|
+
invalidateTable(tableName) {
|
|
249
|
+
let invalidated = 0;
|
|
250
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
251
|
+
if (entry.sql.toLowerCase().includes(tableName.toLowerCase())) {
|
|
252
|
+
this.evict(key, entry);
|
|
253
|
+
invalidated++;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return invalidated;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Remove expired entries
|
|
260
|
+
*/
|
|
261
|
+
prune() {
|
|
262
|
+
const now = Date.now();
|
|
263
|
+
let pruned = 0;
|
|
264
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
265
|
+
if (now - entry.createdAt > this.ttl) {
|
|
266
|
+
this.evict(key, entry);
|
|
267
|
+
pruned++;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return pruned;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get cache size
|
|
274
|
+
*/
|
|
275
|
+
get size() {
|
|
276
|
+
return this.cache.size;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
exports.PreparedStatementCache = PreparedStatementCache;
|
|
280
|
+
/**
|
|
281
|
+
* Query result cache for caching query results
|
|
282
|
+
*/
|
|
283
|
+
class QueryResultCache {
|
|
284
|
+
constructor(options = {}) {
|
|
285
|
+
this.cache = new Map();
|
|
286
|
+
this.maxSize = options.maxSize || 100;
|
|
287
|
+
this.ttl = options.ttl || 60000; // 1 minute default
|
|
288
|
+
this.logging = options.logging || false;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Get cached query result
|
|
292
|
+
*/
|
|
293
|
+
get(sql, params) {
|
|
294
|
+
const key = this.generateKey(sql, params);
|
|
295
|
+
const entry = this.cache.get(key);
|
|
296
|
+
if (!entry)
|
|
297
|
+
return undefined;
|
|
298
|
+
// Check if entry has expired
|
|
299
|
+
if (Date.now() - entry.createdAt > this.ttl) {
|
|
300
|
+
this.cache.delete(key);
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
entry.hitCount++;
|
|
304
|
+
entry.lastUsedAt = Date.now();
|
|
305
|
+
return entry.result;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Store a query result in cache
|
|
309
|
+
*/
|
|
310
|
+
set(sql, params, result) {
|
|
311
|
+
const key = this.generateKey(sql, params);
|
|
312
|
+
// Check if we need to evict entries
|
|
313
|
+
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
314
|
+
this.evictOldest();
|
|
315
|
+
}
|
|
316
|
+
this.cache.set(key, {
|
|
317
|
+
key,
|
|
318
|
+
sql,
|
|
319
|
+
params,
|
|
320
|
+
result,
|
|
321
|
+
createdAt: Date.now(),
|
|
322
|
+
lastUsedAt: Date.now(),
|
|
323
|
+
hitCount: 0,
|
|
324
|
+
});
|
|
325
|
+
this.log(`Cached result for: ${key}`);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Check if a query result is cached
|
|
329
|
+
*/
|
|
330
|
+
has(sql, params) {
|
|
331
|
+
const key = this.generateKey(sql, params);
|
|
332
|
+
const entry = this.cache.get(key);
|
|
333
|
+
if (!entry)
|
|
334
|
+
return false;
|
|
335
|
+
if (Date.now() - entry.createdAt > this.ttl) {
|
|
336
|
+
this.cache.delete(key);
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Invalidate cache for a specific table
|
|
343
|
+
*/
|
|
344
|
+
invalidateTable(tableName) {
|
|
345
|
+
let invalidated = 0;
|
|
346
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
347
|
+
if (entry.sql.toLowerCase().includes(tableName.toLowerCase())) {
|
|
348
|
+
this.cache.delete(key);
|
|
349
|
+
invalidated++;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return invalidated;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Clear all cached results
|
|
356
|
+
*/
|
|
357
|
+
clear() {
|
|
358
|
+
this.cache.clear();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Get cache statistics
|
|
362
|
+
*/
|
|
363
|
+
getStats() {
|
|
364
|
+
let hitCount = 0;
|
|
365
|
+
for (const entry of this.cache.values()) {
|
|
366
|
+
hitCount += entry.hitCount;
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
size: this.cache.size,
|
|
370
|
+
maxSize: this.maxSize,
|
|
371
|
+
hitCount,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Generate a cache key
|
|
376
|
+
*/
|
|
377
|
+
generateKey(sql, params) {
|
|
378
|
+
const normalizedSql = sql.replace(/\s+/g, ' ').trim();
|
|
379
|
+
if (params && params.length > 0) {
|
|
380
|
+
return `${normalizedSql}::${JSON.stringify(params)}`;
|
|
381
|
+
}
|
|
382
|
+
return normalizedSql;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Evict the oldest entry
|
|
386
|
+
*/
|
|
387
|
+
evictOldest() {
|
|
388
|
+
let oldestKey = null;
|
|
389
|
+
let oldestTime = Date.now();
|
|
390
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
391
|
+
if (entry.lastUsedAt < oldestTime) {
|
|
392
|
+
oldestTime = entry.lastUsedAt;
|
|
393
|
+
oldestKey = key;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (oldestKey) {
|
|
397
|
+
this.cache.delete(oldestKey);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Log a message
|
|
402
|
+
*/
|
|
403
|
+
log(message) {
|
|
404
|
+
if (this.logging) {
|
|
405
|
+
console.log(`[QueryResultCache] ${message}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
exports.QueryResultCache = QueryResultCache;
|
|
410
|
+
/**
|
|
411
|
+
* Create a prepared statement cache
|
|
412
|
+
*/
|
|
413
|
+
function createPreparedStatementCache(dialect, options) {
|
|
414
|
+
return new PreparedStatementCache(dialect, {
|
|
415
|
+
maxSize: 500,
|
|
416
|
+
...options,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
exports.default = {
|
|
420
|
+
PreparedStatementCache,
|
|
421
|
+
QueryResultCache,
|
|
422
|
+
createPreparedStatementCache,
|
|
423
|
+
};
|