@agentforge/tools 0.16.38 → 0.16.40
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/dist/index.cjs +356 -419
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -162
- package/dist/index.d.ts +17 -162
- package/dist/index.js +356 -419
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4199,6 +4199,59 @@ function scheduleReconnection(context, logger23) {
|
|
|
4199
4199
|
}, delay2);
|
|
4200
4200
|
context.setReconnectionTimer(timer);
|
|
4201
4201
|
}
|
|
4202
|
+
|
|
4203
|
+
// src/data/relational/connection/connection-cleanup.ts
|
|
4204
|
+
async function cleanupConnectionResources(vendor, client, clearConnection, logger23) {
|
|
4205
|
+
if (!client) {
|
|
4206
|
+
return;
|
|
4207
|
+
}
|
|
4208
|
+
logger23.debug("Cleaning up cancelled connection", {
|
|
4209
|
+
vendor
|
|
4210
|
+
});
|
|
4211
|
+
try {
|
|
4212
|
+
await shutdownClient(vendor, client);
|
|
4213
|
+
} catch (error) {
|
|
4214
|
+
logger23.debug("Error during cancelled connection cleanup", {
|
|
4215
|
+
vendor,
|
|
4216
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4217
|
+
});
|
|
4218
|
+
} finally {
|
|
4219
|
+
clearConnection();
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
async function closeManagedConnection(runtime, logger23) {
|
|
4223
|
+
const client = runtime.getClient();
|
|
4224
|
+
if (client) {
|
|
4225
|
+
logger23.info("Closing database connection", {
|
|
4226
|
+
vendor: runtime.vendor,
|
|
4227
|
+
state: runtime.getState()
|
|
4228
|
+
});
|
|
4229
|
+
try {
|
|
4230
|
+
await shutdownClient(runtime.vendor, client);
|
|
4231
|
+
runtime.setState("disconnected" /* DISCONNECTED */);
|
|
4232
|
+
runtime.emitDisconnected();
|
|
4233
|
+
logger23.debug("Database connection closed successfully", {
|
|
4234
|
+
vendor: runtime.vendor,
|
|
4235
|
+
state: runtime.getState()
|
|
4236
|
+
});
|
|
4237
|
+
} catch (error) {
|
|
4238
|
+
logger23.error("Error closing database connection", {
|
|
4239
|
+
vendor: runtime.vendor,
|
|
4240
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4241
|
+
state: runtime.getState()
|
|
4242
|
+
});
|
|
4243
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
4244
|
+
runtime.setState("error" /* ERROR */);
|
|
4245
|
+
runtime.emitError(normalizedError);
|
|
4246
|
+
} finally {
|
|
4247
|
+
runtime.setClient(null);
|
|
4248
|
+
runtime.setDb(null);
|
|
4249
|
+
}
|
|
4250
|
+
} else if (runtime.getState() !== "disconnected" /* DISCONNECTED */) {
|
|
4251
|
+
runtime.setState("disconnected" /* DISCONNECTED */);
|
|
4252
|
+
runtime.emitDisconnected();
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4202
4255
|
var logger7 = createLogger("agentforge:tools:data:relational:connection:vendor-init");
|
|
4203
4256
|
var SAFE_INITIALIZATION_PATTERNS = [
|
|
4204
4257
|
"Pool max connections must be",
|
|
@@ -4354,6 +4407,190 @@ async function initializeSQLiteConnection(connection) {
|
|
|
4354
4407
|
return { client, db };
|
|
4355
4408
|
}
|
|
4356
4409
|
|
|
4410
|
+
// src/data/relational/connection/connection-initialization.ts
|
|
4411
|
+
async function initializeManagedConnection(runtime, logger23) {
|
|
4412
|
+
const startTime = Date.now();
|
|
4413
|
+
const currentGeneration = runtime.getConnectionGeneration();
|
|
4414
|
+
if (runtime.getState() === "connected" /* CONNECTED */ && runtime.getClient()) {
|
|
4415
|
+
logger23.warn("Re-initializing an already connected manager; emitting disconnected before cleanup", {
|
|
4416
|
+
vendor: runtime.vendor
|
|
4417
|
+
});
|
|
4418
|
+
runtime.setState("disconnected" /* DISCONNECTED */);
|
|
4419
|
+
runtime.emitDisconnected();
|
|
4420
|
+
await cleanupConnectionResources(
|
|
4421
|
+
runtime.vendor,
|
|
4422
|
+
runtime.getClient(),
|
|
4423
|
+
() => {
|
|
4424
|
+
runtime.setClient(null);
|
|
4425
|
+
runtime.setDb(null);
|
|
4426
|
+
},
|
|
4427
|
+
logger23
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
runtime.setState("connecting" /* CONNECTING */);
|
|
4431
|
+
logger23.info("Initializing database connection", {
|
|
4432
|
+
vendor: runtime.vendor,
|
|
4433
|
+
connectionType: typeof runtime.config.connection,
|
|
4434
|
+
state: runtime.getState()
|
|
4435
|
+
});
|
|
4436
|
+
try {
|
|
4437
|
+
const initialized = await initializeVendorConnection(runtime.config);
|
|
4438
|
+
runtime.setClient(initialized.client);
|
|
4439
|
+
runtime.setDb(initialized.db);
|
|
4440
|
+
if (currentGeneration !== runtime.getConnectionGeneration()) {
|
|
4441
|
+
logger23.debug("Connection cancelled during initialization", {
|
|
4442
|
+
vendor: runtime.vendor,
|
|
4443
|
+
currentGeneration,
|
|
4444
|
+
newGeneration: runtime.getConnectionGeneration()
|
|
4445
|
+
});
|
|
4446
|
+
await cleanupConnectionResources(
|
|
4447
|
+
runtime.vendor,
|
|
4448
|
+
runtime.getClient(),
|
|
4449
|
+
() => {
|
|
4450
|
+
runtime.setClient(null);
|
|
4451
|
+
runtime.setDb(null);
|
|
4452
|
+
},
|
|
4453
|
+
logger23
|
|
4454
|
+
);
|
|
4455
|
+
runtime.setState("disconnected" /* DISCONNECTED */);
|
|
4456
|
+
throw new Error("Connection cancelled during initialization");
|
|
4457
|
+
}
|
|
4458
|
+
logger23.debug("Validating connection health", { vendor: runtime.vendor });
|
|
4459
|
+
const healthy = await runtime.isHealthy();
|
|
4460
|
+
if (!healthy) {
|
|
4461
|
+
throw new Error(`Failed to establish healthy connection to ${runtime.vendor} database`);
|
|
4462
|
+
}
|
|
4463
|
+
if (currentGeneration !== runtime.getConnectionGeneration()) {
|
|
4464
|
+
logger23.debug("Connection cancelled during health check", {
|
|
4465
|
+
vendor: runtime.vendor,
|
|
4466
|
+
currentGeneration,
|
|
4467
|
+
newGeneration: runtime.getConnectionGeneration()
|
|
4468
|
+
});
|
|
4469
|
+
await cleanupConnectionResources(
|
|
4470
|
+
runtime.vendor,
|
|
4471
|
+
runtime.getClient(),
|
|
4472
|
+
() => {
|
|
4473
|
+
runtime.setClient(null);
|
|
4474
|
+
runtime.setDb(null);
|
|
4475
|
+
},
|
|
4476
|
+
logger23
|
|
4477
|
+
);
|
|
4478
|
+
runtime.setState("disconnected" /* DISCONNECTED */);
|
|
4479
|
+
throw new Error("Connection cancelled during health check");
|
|
4480
|
+
}
|
|
4481
|
+
runtime.setState("connected" /* CONNECTED */);
|
|
4482
|
+
runtime.emitConnected();
|
|
4483
|
+
runtime.resetReconnectionAttempts();
|
|
4484
|
+
logger23.info("Database connection initialized successfully", {
|
|
4485
|
+
vendor: runtime.vendor,
|
|
4486
|
+
duration: Date.now() - startTime,
|
|
4487
|
+
state: runtime.getState()
|
|
4488
|
+
});
|
|
4489
|
+
} catch (error) {
|
|
4490
|
+
const errorMessage = `Failed to initialize ${runtime.vendor} connection`;
|
|
4491
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
4492
|
+
const isValidationError = SAFE_INITIALIZATION_PATTERNS.some(
|
|
4493
|
+
(pattern) => normalizedError.message.includes(pattern)
|
|
4494
|
+
);
|
|
4495
|
+
if (isValidationError) {
|
|
4496
|
+
runtime.setState("error" /* ERROR */);
|
|
4497
|
+
throw normalizedError;
|
|
4498
|
+
}
|
|
4499
|
+
const isCancellation = normalizedError.message.includes("Connection cancelled");
|
|
4500
|
+
if (!isCancellation) {
|
|
4501
|
+
runtime.setState("error" /* ERROR */);
|
|
4502
|
+
if (runtime.errorListenerCount() > 0) {
|
|
4503
|
+
runtime.emitError(normalizedError);
|
|
4504
|
+
}
|
|
4505
|
+
logger23.error(errorMessage, {
|
|
4506
|
+
vendor: runtime.vendor,
|
|
4507
|
+
error: normalizedError.message,
|
|
4508
|
+
duration: Date.now() - startTime,
|
|
4509
|
+
state: runtime.getState()
|
|
4510
|
+
});
|
|
4511
|
+
await cleanupConnectionResources(
|
|
4512
|
+
runtime.vendor,
|
|
4513
|
+
runtime.getClient(),
|
|
4514
|
+
() => {
|
|
4515
|
+
runtime.setClient(null);
|
|
4516
|
+
runtime.setDb(null);
|
|
4517
|
+
},
|
|
4518
|
+
logger23
|
|
4519
|
+
);
|
|
4520
|
+
if (runtime.getReconnectionConfig().enabled && currentGeneration === runtime.getConnectionGeneration()) {
|
|
4521
|
+
runtime.scheduleReconnection();
|
|
4522
|
+
}
|
|
4523
|
+
} else {
|
|
4524
|
+
logger23.debug("Connection initialization cancelled", {
|
|
4525
|
+
vendor: runtime.vendor,
|
|
4526
|
+
duration: Date.now() - startTime,
|
|
4527
|
+
state: runtime.getState()
|
|
4528
|
+
});
|
|
4529
|
+
}
|
|
4530
|
+
if (error instanceof Error) {
|
|
4531
|
+
throw new Error(errorMessage, { cause: error });
|
|
4532
|
+
}
|
|
4533
|
+
throw new Error(`${errorMessage}: ${String(error)}`);
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4536
|
+
function isSqliteNonQueryError(error) {
|
|
4537
|
+
if (!(error instanceof Error)) return false;
|
|
4538
|
+
const isSqliteOrTypeError = error.constructor?.name === "SqliteError" || error.name === "SqliteError" || error instanceof TypeError;
|
|
4539
|
+
const hasNonQueryMessage = error.message.includes("does not return data");
|
|
4540
|
+
return isSqliteOrTypeError && hasNonQueryMessage;
|
|
4541
|
+
}
|
|
4542
|
+
async function checkConnectionHealth(args, logger23) {
|
|
4543
|
+
if (!args.db || !args.client) {
|
|
4544
|
+
logger23.debug("Health check failed: connection not initialized", { vendor: args.vendor });
|
|
4545
|
+
return false;
|
|
4546
|
+
}
|
|
4547
|
+
try {
|
|
4548
|
+
await args.execute(sql`SELECT 1`);
|
|
4549
|
+
logger23.debug("Health check passed", { vendor: args.vendor });
|
|
4550
|
+
return true;
|
|
4551
|
+
} catch (error) {
|
|
4552
|
+
logger23.debug("Health check failed", {
|
|
4553
|
+
vendor: args.vendor,
|
|
4554
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4555
|
+
});
|
|
4556
|
+
return false;
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
function getConnectionPoolMetrics(vendor, client) {
|
|
4560
|
+
if (!client) {
|
|
4561
|
+
return { totalCount: 0, activeCount: 0, idleCount: 0, waitingCount: 0 };
|
|
4562
|
+
}
|
|
4563
|
+
if (vendor === "postgresql") {
|
|
4564
|
+
const pgClient = client;
|
|
4565
|
+
const totalCount2 = pgClient.totalCount || 0;
|
|
4566
|
+
const idleCount = pgClient.idleCount || 0;
|
|
4567
|
+
const waitingCount = pgClient.waitingCount || 0;
|
|
4568
|
+
const activeCount = Math.max(totalCount2 - idleCount, 0);
|
|
4569
|
+
return {
|
|
4570
|
+
totalCount: totalCount2,
|
|
4571
|
+
activeCount,
|
|
4572
|
+
idleCount,
|
|
4573
|
+
waitingCount
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
if (vendor === "mysql") {
|
|
4577
|
+
return {
|
|
4578
|
+
totalCount: 0,
|
|
4579
|
+
activeCount: 0,
|
|
4580
|
+
idleCount: 0,
|
|
4581
|
+
waitingCount: 0
|
|
4582
|
+
};
|
|
4583
|
+
}
|
|
4584
|
+
const sqliteClient = client;
|
|
4585
|
+
const totalCount = sqliteClient.open ? 1 : 0;
|
|
4586
|
+
return {
|
|
4587
|
+
totalCount,
|
|
4588
|
+
activeCount: totalCount,
|
|
4589
|
+
idleCount: 0,
|
|
4590
|
+
waitingCount: 0
|
|
4591
|
+
};
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4357
4594
|
// src/data/relational/connection/query-execution.ts
|
|
4358
4595
|
async function executeQuery(context, query, logger23) {
|
|
4359
4596
|
logger23.debug("Executing SQL query", {
|
|
@@ -4373,11 +4610,11 @@ async function executeQuery(context, query, logger23) {
|
|
|
4373
4610
|
}
|
|
4374
4611
|
return context.db.execute(query);
|
|
4375
4612
|
}
|
|
4376
|
-
async function executeSqliteQuery(db, query,
|
|
4613
|
+
async function executeSqliteQuery(db, query, isSqliteNonQueryError2) {
|
|
4377
4614
|
try {
|
|
4378
4615
|
return db.all(query);
|
|
4379
4616
|
} catch (error) {
|
|
4380
|
-
if (
|
|
4617
|
+
if (isSqliteNonQueryError2(error)) {
|
|
4381
4618
|
const runResult = db.run(query);
|
|
4382
4619
|
return { ...runResult, affectedRows: runResult.changes ?? 0 };
|
|
4383
4620
|
}
|
|
@@ -4442,11 +4679,6 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4442
4679
|
// Track in-flight connection attempts
|
|
4443
4680
|
connectionGeneration = 0;
|
|
4444
4681
|
// Cancellation token for disconnect
|
|
4445
|
-
/**
|
|
4446
|
-
* Create a new ConnectionManager instance
|
|
4447
|
-
* @param config - Connection configuration
|
|
4448
|
-
* @param reconnectionConfig - Optional reconnection configuration
|
|
4449
|
-
*/
|
|
4450
4682
|
constructor(config, reconnectionConfig) {
|
|
4451
4683
|
super();
|
|
4452
4684
|
this.config = config;
|
|
@@ -4459,11 +4691,6 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4459
4691
|
};
|
|
4460
4692
|
checkPeerDependency(this.vendor);
|
|
4461
4693
|
}
|
|
4462
|
-
/**
|
|
4463
|
-
* Connect to the database
|
|
4464
|
-
* Initializes the connection and sets up automatic reconnection if configured
|
|
4465
|
-
* @throws {Error} If connection fails or configuration is invalid
|
|
4466
|
-
*/
|
|
4467
4694
|
async connect() {
|
|
4468
4695
|
if (this.state === "connected" /* CONNECTED */) {
|
|
4469
4696
|
logger8.debug("Already connected", { vendor: this.vendor });
|
|
@@ -4487,13 +4714,6 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4487
4714
|
});
|
|
4488
4715
|
return this.connectPromise;
|
|
4489
4716
|
}
|
|
4490
|
-
/**
|
|
4491
|
-
* Disconnect from the database
|
|
4492
|
-
* Closes the connection and cancels any pending reconnection attempts
|
|
4493
|
-
*
|
|
4494
|
-
* Note: Event listeners are NOT removed by this method. If you need to dispose
|
|
4495
|
-
* of the ConnectionManager instance entirely, call dispose() instead.
|
|
4496
|
-
*/
|
|
4497
4717
|
async disconnect() {
|
|
4498
4718
|
this.connectionGeneration++;
|
|
4499
4719
|
this.reconnectionTimer = cancelPendingReconnection(
|
|
@@ -4511,142 +4731,22 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4511
4731
|
);
|
|
4512
4732
|
await this.close();
|
|
4513
4733
|
}
|
|
4514
|
-
/**
|
|
4515
|
-
* Dispose of the ConnectionManager instance
|
|
4516
|
-
* Disconnects and removes all event listeners
|
|
4517
|
-
* Call this when you're done with the ConnectionManager and won't reuse it
|
|
4518
|
-
*/
|
|
4519
4734
|
async dispose() {
|
|
4520
4735
|
await this.disconnect();
|
|
4521
4736
|
this.removeAllListeners();
|
|
4522
4737
|
}
|
|
4523
|
-
/**
|
|
4524
|
-
* Check if the connection is currently connected
|
|
4525
|
-
* @returns true if connected, false otherwise
|
|
4526
|
-
*/
|
|
4527
4738
|
isConnected() {
|
|
4528
4739
|
return this.state === "connected" /* CONNECTED */;
|
|
4529
4740
|
}
|
|
4530
|
-
/**
|
|
4531
|
-
* Get the current connection state
|
|
4532
|
-
* @returns Current connection state
|
|
4533
|
-
*/
|
|
4534
4741
|
getState() {
|
|
4535
4742
|
return this.state;
|
|
4536
4743
|
}
|
|
4537
|
-
/**
|
|
4538
|
-
* Get the configured database vendor.
|
|
4539
|
-
*/
|
|
4540
4744
|
getVendor() {
|
|
4541
4745
|
return this.vendor;
|
|
4542
4746
|
}
|
|
4543
|
-
/**
|
|
4544
|
-
* Initialize the database connection
|
|
4545
|
-
*
|
|
4546
|
-
* This method is public to maintain compatibility with the DatabaseConnection interface
|
|
4547
|
-
* and existing code (e.g., relational-query.ts). For new code, prefer using connect()
|
|
4548
|
-
* which provides lifecycle management and automatic reconnection.
|
|
4549
|
-
*
|
|
4550
|
-
* @throws {Error} If connection fails or configuration is invalid
|
|
4551
|
-
*/
|
|
4552
4747
|
async initialize() {
|
|
4553
|
-
|
|
4554
|
-
const currentGeneration = this.connectionGeneration;
|
|
4555
|
-
if (this.state === "connected" /* CONNECTED */ && this.client) {
|
|
4556
|
-
logger8.warn("Re-initializing an already connected manager; emitting disconnected before cleanup", {
|
|
4557
|
-
vendor: this.vendor
|
|
4558
|
-
});
|
|
4559
|
-
this.setState("disconnected" /* DISCONNECTED */);
|
|
4560
|
-
this.emit("disconnected");
|
|
4561
|
-
await this.cleanupCancelledConnection();
|
|
4562
|
-
}
|
|
4563
|
-
this.setState("connecting" /* CONNECTING */);
|
|
4564
|
-
logger8.info("Initializing database connection", {
|
|
4565
|
-
vendor: this.vendor,
|
|
4566
|
-
connectionType: typeof this.config.connection,
|
|
4567
|
-
state: this.state
|
|
4568
|
-
});
|
|
4569
|
-
try {
|
|
4570
|
-
const initialized = await initializeVendorConnection(this.config);
|
|
4571
|
-
this.client = initialized.client;
|
|
4572
|
-
this.db = initialized.db;
|
|
4573
|
-
if (currentGeneration !== this.connectionGeneration) {
|
|
4574
|
-
logger8.debug("Connection cancelled during initialization", {
|
|
4575
|
-
vendor: this.vendor,
|
|
4576
|
-
currentGeneration,
|
|
4577
|
-
newGeneration: this.connectionGeneration
|
|
4578
|
-
});
|
|
4579
|
-
await this.cleanupCancelledConnection();
|
|
4580
|
-
this.setState("disconnected" /* DISCONNECTED */);
|
|
4581
|
-
throw new Error("Connection cancelled during initialization");
|
|
4582
|
-
}
|
|
4583
|
-
logger8.debug("Validating connection health", { vendor: this.vendor });
|
|
4584
|
-
const healthy = await this.isHealthy();
|
|
4585
|
-
if (!healthy) {
|
|
4586
|
-
throw new Error(`Failed to establish healthy connection to ${this.vendor} database`);
|
|
4587
|
-
}
|
|
4588
|
-
if (currentGeneration !== this.connectionGeneration) {
|
|
4589
|
-
logger8.debug("Connection cancelled during health check", {
|
|
4590
|
-
vendor: this.vendor,
|
|
4591
|
-
currentGeneration,
|
|
4592
|
-
newGeneration: this.connectionGeneration
|
|
4593
|
-
});
|
|
4594
|
-
await this.cleanupCancelledConnection();
|
|
4595
|
-
this.setState("disconnected" /* DISCONNECTED */);
|
|
4596
|
-
throw new Error("Connection cancelled during health check");
|
|
4597
|
-
}
|
|
4598
|
-
this.setState("connected" /* CONNECTED */);
|
|
4599
|
-
this.emit("connected");
|
|
4600
|
-
this.reconnectionAttempts = 0;
|
|
4601
|
-
logger8.info("Database connection initialized successfully", {
|
|
4602
|
-
vendor: this.vendor,
|
|
4603
|
-
duration: Date.now() - startTime,
|
|
4604
|
-
state: this.state
|
|
4605
|
-
});
|
|
4606
|
-
} catch (error) {
|
|
4607
|
-
const errorMessage = `Failed to initialize ${this.vendor} connection`;
|
|
4608
|
-
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
4609
|
-
const isValidationError = SAFE_INITIALIZATION_PATTERNS.some(
|
|
4610
|
-
(pattern) => normalizedError.message.includes(pattern)
|
|
4611
|
-
);
|
|
4612
|
-
if (isValidationError) {
|
|
4613
|
-
this.setState("error" /* ERROR */);
|
|
4614
|
-
throw normalizedError;
|
|
4615
|
-
}
|
|
4616
|
-
const isCancellation = normalizedError.message.includes("Connection cancelled");
|
|
4617
|
-
if (!isCancellation) {
|
|
4618
|
-
this.setState("error" /* ERROR */);
|
|
4619
|
-
if (this.listenerCount("error") > 0) {
|
|
4620
|
-
this.emit("error", normalizedError);
|
|
4621
|
-
}
|
|
4622
|
-
logger8.error(errorMessage, {
|
|
4623
|
-
vendor: this.vendor,
|
|
4624
|
-
error: normalizedError.message,
|
|
4625
|
-
duration: Date.now() - startTime,
|
|
4626
|
-
state: this.state
|
|
4627
|
-
});
|
|
4628
|
-
await this.cleanupCancelledConnection();
|
|
4629
|
-
if (this.reconnectionConfig.enabled && currentGeneration === this.connectionGeneration) {
|
|
4630
|
-
this.scheduleReconnection();
|
|
4631
|
-
}
|
|
4632
|
-
} else {
|
|
4633
|
-
logger8.debug("Connection initialization cancelled", {
|
|
4634
|
-
vendor: this.vendor,
|
|
4635
|
-
duration: Date.now() - startTime,
|
|
4636
|
-
state: this.state
|
|
4637
|
-
});
|
|
4638
|
-
}
|
|
4639
|
-
if (error instanceof Error) {
|
|
4640
|
-
throw new Error(errorMessage, { cause: error });
|
|
4641
|
-
}
|
|
4642
|
-
throw new Error(`${errorMessage}: ${String(error)}`);
|
|
4643
|
-
}
|
|
4748
|
+
return initializeManagedConnection(this.createRuntimeAdapter(), logger8);
|
|
4644
4749
|
}
|
|
4645
|
-
/**
|
|
4646
|
-
* Set the connection state and log the change
|
|
4647
|
-
* @param newState - New connection state
|
|
4648
|
-
* @private
|
|
4649
|
-
*/
|
|
4650
4750
|
setState(newState) {
|
|
4651
4751
|
const oldState = this.state;
|
|
4652
4752
|
this.state = newState;
|
|
@@ -4658,10 +4758,6 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4658
4758
|
});
|
|
4659
4759
|
}
|
|
4660
4760
|
}
|
|
4661
|
-
/**
|
|
4662
|
-
* Schedule a reconnection attempt with exponential backoff
|
|
4663
|
-
* @private
|
|
4664
|
-
*/
|
|
4665
4761
|
scheduleReconnection() {
|
|
4666
4762
|
scheduleReconnection(
|
|
4667
4763
|
{
|
|
@@ -4688,40 +4784,39 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4688
4784
|
logger8
|
|
4689
4785
|
);
|
|
4690
4786
|
}
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4787
|
+
createRuntimeAdapter() {
|
|
4788
|
+
return {
|
|
4789
|
+
vendor: this.vendor,
|
|
4790
|
+
config: this.config,
|
|
4791
|
+
getState: () => this.state,
|
|
4792
|
+
setState: (state) => this.setState(state),
|
|
4793
|
+
getClient: () => this.client,
|
|
4794
|
+
setClient: (client) => {
|
|
4795
|
+
this.client = client;
|
|
4796
|
+
},
|
|
4797
|
+
getDb: () => this.db,
|
|
4798
|
+
setDb: (db) => {
|
|
4799
|
+
this.db = db;
|
|
4800
|
+
},
|
|
4801
|
+
emitConnected: () => {
|
|
4802
|
+
this.emit("connected");
|
|
4803
|
+
},
|
|
4804
|
+
emitDisconnected: () => {
|
|
4805
|
+
this.emit("disconnected");
|
|
4806
|
+
},
|
|
4807
|
+
emitError: (error) => {
|
|
4808
|
+
this.emit("error", error);
|
|
4809
|
+
},
|
|
4810
|
+
errorListenerCount: () => this.listenerCount("error"),
|
|
4811
|
+
getReconnectionConfig: () => this.reconnectionConfig,
|
|
4812
|
+
scheduleReconnection: () => this.scheduleReconnection(),
|
|
4813
|
+
resetReconnectionAttempts: () => {
|
|
4814
|
+
this.reconnectionAttempts = 0;
|
|
4815
|
+
},
|
|
4816
|
+
getConnectionGeneration: () => this.connectionGeneration,
|
|
4817
|
+
isHealthy: () => this.isHealthy()
|
|
4818
|
+
};
|
|
4706
4819
|
}
|
|
4707
|
-
/**
|
|
4708
|
-
* Execute a SQL query
|
|
4709
|
-
*
|
|
4710
|
-
* Executes a parameterized SQL query using Drizzle's SQL template objects.
|
|
4711
|
-
* This method provides safe parameter binding to prevent SQL injection.
|
|
4712
|
-
*
|
|
4713
|
-
* @param query - Drizzle SQL template object with parameter binding
|
|
4714
|
-
* @returns Query result
|
|
4715
|
-
*
|
|
4716
|
-
* @example
|
|
4717
|
-
* ```typescript
|
|
4718
|
-
* import { sql } from 'drizzle-orm';
|
|
4719
|
-
*
|
|
4720
|
-
* const result = await manager.execute(
|
|
4721
|
-
* sql`SELECT * FROM users WHERE id = ${userId}`
|
|
4722
|
-
* );
|
|
4723
|
-
* ```
|
|
4724
|
-
*/
|
|
4725
4820
|
async execute(query) {
|
|
4726
4821
|
if (!this.db) {
|
|
4727
4822
|
throw new Error("Database not initialized. Call initialize() first.");
|
|
@@ -4730,18 +4825,12 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4730
4825
|
{
|
|
4731
4826
|
vendor: this.vendor,
|
|
4732
4827
|
db: this.db,
|
|
4733
|
-
isSqliteNonQueryError
|
|
4828
|
+
isSqliteNonQueryError
|
|
4734
4829
|
},
|
|
4735
4830
|
query,
|
|
4736
4831
|
logger8
|
|
4737
4832
|
);
|
|
4738
4833
|
}
|
|
4739
|
-
/**
|
|
4740
|
-
* Execute a callback with a single dedicated database connection/session.
|
|
4741
|
-
*
|
|
4742
|
-
* This is required for multi-statement transactions so all statements run on
|
|
4743
|
-
* the same underlying connection.
|
|
4744
|
-
*/
|
|
4745
4834
|
async executeInConnection(callback) {
|
|
4746
4835
|
if (!this.client || !this.db) {
|
|
4747
4836
|
throw new Error("Database not initialized. Call connect() first.");
|
|
@@ -4751,65 +4840,14 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4751
4840
|
vendor: this.vendor,
|
|
4752
4841
|
client: this.client,
|
|
4753
4842
|
db: this.db,
|
|
4754
|
-
isSqliteNonQueryError
|
|
4843
|
+
isSqliteNonQueryError
|
|
4755
4844
|
},
|
|
4756
4845
|
callback
|
|
4757
4846
|
);
|
|
4758
4847
|
}
|
|
4759
|
-
/**
|
|
4760
|
-
* Get connection pool metrics
|
|
4761
|
-
*
|
|
4762
|
-
* Returns information about the current state of the connection pool.
|
|
4763
|
-
* For SQLite, returns basic connection status since it uses a single connection.
|
|
4764
|
-
*
|
|
4765
|
-
* @returns Pool metrics including total, active, idle, and waiting connections
|
|
4766
|
-
*/
|
|
4767
4848
|
getPoolMetrics() {
|
|
4768
|
-
|
|
4769
|
-
return { totalCount: 0, activeCount: 0, idleCount: 0, waitingCount: 0 };
|
|
4770
|
-
}
|
|
4771
|
-
if (this.vendor === "postgresql") {
|
|
4772
|
-
const totalCount = this.client.totalCount || 0;
|
|
4773
|
-
const idleCount = this.client.idleCount || 0;
|
|
4774
|
-
const waitingCount = this.client.waitingCount || 0;
|
|
4775
|
-
const activeCount = Math.max(totalCount - idleCount, 0);
|
|
4776
|
-
return {
|
|
4777
|
-
totalCount,
|
|
4778
|
-
activeCount,
|
|
4779
|
-
idleCount,
|
|
4780
|
-
waitingCount
|
|
4781
|
-
};
|
|
4782
|
-
} else if (this.vendor === "mysql") {
|
|
4783
|
-
return {
|
|
4784
|
-
totalCount: 0,
|
|
4785
|
-
activeCount: 0,
|
|
4786
|
-
idleCount: 0,
|
|
4787
|
-
waitingCount: 0
|
|
4788
|
-
};
|
|
4789
|
-
} else {
|
|
4790
|
-
const totalCount = this.client.open ? 1 : 0;
|
|
4791
|
-
const idleCount = 0;
|
|
4792
|
-
const waitingCount = 0;
|
|
4793
|
-
const activeCount = this.client.open ? 1 : 0;
|
|
4794
|
-
return {
|
|
4795
|
-
totalCount,
|
|
4796
|
-
activeCount,
|
|
4797
|
-
idleCount,
|
|
4798
|
-
waitingCount
|
|
4799
|
-
};
|
|
4800
|
-
}
|
|
4849
|
+
return getConnectionPoolMetrics(this.vendor, this.client);
|
|
4801
4850
|
}
|
|
4802
|
-
/**
|
|
4803
|
-
* Close the database connection
|
|
4804
|
-
*
|
|
4805
|
-
* This method is public to maintain compatibility with the DatabaseConnection interface
|
|
4806
|
-
* and existing code (e.g., relational-query.ts). For new code, prefer using disconnect()
|
|
4807
|
-
* for connection lifecycle management. Note that disconnect() does NOT clean up event
|
|
4808
|
-
* listeners; call dispose() if you need full cleanup including listener removal.
|
|
4809
|
-
*
|
|
4810
|
-
* Note: This method coordinates with in-flight connect()/initialize() operations
|
|
4811
|
-
* and cancels pending reconnection timers to prevent unexpected reconnections.
|
|
4812
|
-
*/
|
|
4813
4851
|
async close() {
|
|
4814
4852
|
this.connectionGeneration++;
|
|
4815
4853
|
this.reconnectionTimer = cancelPendingReconnection(
|
|
@@ -4824,80 +4862,38 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
4824
4862
|
this.vendor,
|
|
4825
4863
|
"close"
|
|
4826
4864
|
);
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
vendor: this.vendor,
|
|
4830
|
-
state: this.state
|
|
4831
|
-
});
|
|
4832
|
-
try {
|
|
4833
|
-
await shutdownClient(this.vendor, this.client);
|
|
4834
|
-
this.setState("disconnected" /* DISCONNECTED */);
|
|
4835
|
-
this.emit("disconnected");
|
|
4836
|
-
logger8.debug("Database connection closed successfully", {
|
|
4837
|
-
vendor: this.vendor,
|
|
4838
|
-
state: this.state
|
|
4839
|
-
});
|
|
4840
|
-
} catch (error) {
|
|
4841
|
-
logger8.error("Error closing database connection", {
|
|
4842
|
-
vendor: this.vendor,
|
|
4843
|
-
error: error instanceof Error ? error.message : String(error),
|
|
4844
|
-
state: this.state
|
|
4845
|
-
});
|
|
4846
|
-
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
4847
|
-
this.setState("error" /* ERROR */);
|
|
4848
|
-
this.emit("error", normalizedError);
|
|
4849
|
-
} finally {
|
|
4850
|
-
this.client = null;
|
|
4851
|
-
this.db = null;
|
|
4852
|
-
}
|
|
4853
|
-
} else if (this.state !== "disconnected" /* DISCONNECTED */) {
|
|
4854
|
-
this.setState("disconnected" /* DISCONNECTED */);
|
|
4855
|
-
this.emit("disconnected");
|
|
4856
|
-
}
|
|
4857
|
-
}
|
|
4858
|
-
/**
|
|
4859
|
-
* Clean up resources when a connection is cancelled during initialization
|
|
4860
|
-
* This prevents connection leaks when disconnect() is called while initialize() is in-flight
|
|
4861
|
-
*/
|
|
4862
|
-
async cleanupCancelledConnection() {
|
|
4863
|
-
if (!this.client) {
|
|
4864
|
-
return;
|
|
4865
|
-
}
|
|
4866
|
-
logger8.debug("Cleaning up cancelled connection", {
|
|
4867
|
-
vendor: this.vendor
|
|
4868
|
-
});
|
|
4869
|
-
try {
|
|
4870
|
-
await shutdownClient(this.vendor, this.client);
|
|
4871
|
-
} catch (error) {
|
|
4872
|
-
logger8.debug("Error during cancelled connection cleanup", {
|
|
4865
|
+
await closeManagedConnection(
|
|
4866
|
+
{
|
|
4873
4867
|
vendor: this.vendor,
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4868
|
+
getState: () => this.state,
|
|
4869
|
+
setState: (state) => this.setState(state),
|
|
4870
|
+
getClient: () => this.client,
|
|
4871
|
+
setClient: (client) => {
|
|
4872
|
+
this.client = client;
|
|
4873
|
+
},
|
|
4874
|
+
setDb: (db) => {
|
|
4875
|
+
this.db = db;
|
|
4876
|
+
},
|
|
4877
|
+
emitDisconnected: () => {
|
|
4878
|
+
this.emit("disconnected");
|
|
4879
|
+
},
|
|
4880
|
+
emitError: (error) => {
|
|
4881
|
+
this.emit("error", error);
|
|
4882
|
+
}
|
|
4883
|
+
},
|
|
4884
|
+
logger8
|
|
4885
|
+
);
|
|
4880
4886
|
}
|
|
4881
|
-
/**
|
|
4882
|
-
* Check if the connection is healthy
|
|
4883
|
-
* @returns true if connection is healthy, false otherwise
|
|
4884
|
-
*/
|
|
4885
4887
|
async isHealthy() {
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
return false;
|
|
4889
|
-
}
|
|
4890
|
-
try {
|
|
4891
|
-
await this.execute(sql`SELECT 1`);
|
|
4892
|
-
logger8.debug("Health check passed", { vendor: this.vendor });
|
|
4893
|
-
return true;
|
|
4894
|
-
} catch (error) {
|
|
4895
|
-
logger8.debug("Health check failed", {
|
|
4888
|
+
return checkConnectionHealth(
|
|
4889
|
+
{
|
|
4896
4890
|
vendor: this.vendor,
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4891
|
+
db: this.db,
|
|
4892
|
+
client: this.client,
|
|
4893
|
+
execute: (query) => this.execute(query)
|
|
4894
|
+
},
|
|
4895
|
+
logger8
|
|
4896
|
+
);
|
|
4901
4897
|
}
|
|
4902
4898
|
};
|
|
4903
4899
|
var logger9 = createLogger("agentforge:tools:data:relational:query");
|
|
@@ -5065,11 +5061,9 @@ function rowsHaveHomogeneousColumns(rows) {
|
|
|
5065
5061
|
function buildSingleRowInsert(table, row, vendor) {
|
|
5066
5062
|
const columns = Object.keys(row);
|
|
5067
5063
|
if (columns.length === 0) {
|
|
5068
|
-
|
|
5069
|
-
return sql.raw(`INSERT INTO ${quotedTable2} DEFAULT VALUES`);
|
|
5064
|
+
return sql.raw(`INSERT INTO ${quoteQualifiedIdentifier(table, vendor)} DEFAULT VALUES`);
|
|
5070
5065
|
}
|
|
5071
5066
|
columns.forEach((column) => validateIdentifier(column, "Insert column"));
|
|
5072
|
-
const quotedTable = quoteQualifiedIdentifier(table, vendor);
|
|
5073
5067
|
const quotedColumns = columns.map((column) => quoteIdentifier(column, vendor)).join(", ");
|
|
5074
5068
|
const valueFragments = columns.map((column) => {
|
|
5075
5069
|
const value = row[column];
|
|
@@ -5080,7 +5074,9 @@ function buildSingleRowInsert(table, row, vendor) {
|
|
|
5080
5074
|
});
|
|
5081
5075
|
return sql.join(
|
|
5082
5076
|
[
|
|
5083
|
-
sql.raw(
|
|
5077
|
+
sql.raw(
|
|
5078
|
+
`INSERT INTO ${quoteQualifiedIdentifier(table, vendor)} (${quotedColumns}) VALUES (`
|
|
5079
|
+
),
|
|
5084
5080
|
sql.join(valueFragments, sql.raw(", ")),
|
|
5085
5081
|
sql.raw(")")
|
|
5086
5082
|
],
|
|
@@ -5091,12 +5087,13 @@ function buildInsertValuesQuery(table, columns, rows, vendor) {
|
|
|
5091
5087
|
if (vendor === "sqlite" && !rowsHaveHomogeneousColumns(rows)) {
|
|
5092
5088
|
return rows.map((row) => buildSingleRowInsert(table, row, vendor));
|
|
5093
5089
|
}
|
|
5094
|
-
const quotedTable = quoteQualifiedIdentifier(table, vendor);
|
|
5095
5090
|
const quotedColumns = columns.map((column) => quoteIdentifier(column, vendor)).join(", ");
|
|
5096
5091
|
const rowTuples = rows.map((row) => buildValuesTuple(columns, row));
|
|
5097
5092
|
return sql.join(
|
|
5098
5093
|
[
|
|
5099
|
-
sql.raw(
|
|
5094
|
+
sql.raw(
|
|
5095
|
+
`INSERT INTO ${quoteQualifiedIdentifier(table, vendor)} (${quotedColumns}) VALUES `
|
|
5096
|
+
),
|
|
5100
5097
|
sql.join(rowTuples, sql.raw(", "))
|
|
5101
5098
|
],
|
|
5102
5099
|
sql.raw("")
|
|
@@ -5106,8 +5103,7 @@ function buildInsertDefaultValuesQuery(table, rowCount, vendor) {
|
|
|
5106
5103
|
if (rowCount > 1) {
|
|
5107
5104
|
throw new Error("Batch INSERT with only DEFAULT VALUES is not supported");
|
|
5108
5105
|
}
|
|
5109
|
-
|
|
5110
|
-
return sql.raw(`INSERT INTO ${quotedTable} DEFAULT VALUES`);
|
|
5106
|
+
return sql.raw(`INSERT INTO ${quoteQualifiedIdentifier(table, vendor)} DEFAULT VALUES`);
|
|
5111
5107
|
}
|
|
5112
5108
|
function buildInsertQuery(input) {
|
|
5113
5109
|
validateQualifiedIdentifier(input.table, "Table name");
|
|
@@ -5130,9 +5126,7 @@ function buildInsertQuery(input) {
|
|
|
5130
5126
|
let queries = valuesResult;
|
|
5131
5127
|
if (returningMode !== "none" && supportsReturning) {
|
|
5132
5128
|
const returningClause = returningMode === "id" ? sql.raw(` RETURNING ${quoteIdentifier(idColumn, input.vendor)}`) : sql.raw(" RETURNING *");
|
|
5133
|
-
queries = queries.map(
|
|
5134
|
-
(q) => sql.join([q, returningClause], sql.raw(""))
|
|
5135
|
-
);
|
|
5129
|
+
queries = queries.map((query2) => sql.join([query2, returningClause], sql.raw("")));
|
|
5136
5130
|
}
|
|
5137
5131
|
return {
|
|
5138
5132
|
query: queries,
|
|
@@ -5144,14 +5138,10 @@ function buildInsertQuery(input) {
|
|
|
5144
5138
|
}
|
|
5145
5139
|
let query = valuesResult;
|
|
5146
5140
|
if (returningMode !== "none" && supportsReturning) {
|
|
5147
|
-
|
|
5148
|
-
query
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
);
|
|
5152
|
-
} else {
|
|
5153
|
-
query = sql.join([query, sql.raw(" RETURNING *")], sql.raw(""));
|
|
5154
|
-
}
|
|
5141
|
+
query = returningMode === "id" ? sql.join(
|
|
5142
|
+
[query, sql.raw(` RETURNING ${quoteIdentifier(idColumn, input.vendor)}`)],
|
|
5143
|
+
sql.raw("")
|
|
5144
|
+
) : sql.join([query, sql.raw(" RETURNING *")], sql.raw(""));
|
|
5155
5145
|
}
|
|
5156
5146
|
return {
|
|
5157
5147
|
query,
|
|
@@ -5161,23 +5151,7 @@ function buildInsertQuery(input) {
|
|
|
5161
5151
|
supportsReturning
|
|
5162
5152
|
};
|
|
5163
5153
|
}
|
|
5164
|
-
function
|
|
5165
|
-
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
5166
|
-
throw new Error("Update data must be an object");
|
|
5167
|
-
}
|
|
5168
|
-
const keys = Object.keys(data);
|
|
5169
|
-
if (keys.length === 0) {
|
|
5170
|
-
throw new Error("Update data must not be empty");
|
|
5171
|
-
}
|
|
5172
|
-
keys.forEach((column) => {
|
|
5173
|
-
validateIdentifier(column, "Update column");
|
|
5174
|
-
if (data[column] === void 0) {
|
|
5175
|
-
throw new Error(`Update value for column "${column}" must not be undefined`);
|
|
5176
|
-
}
|
|
5177
|
-
});
|
|
5178
|
-
return data;
|
|
5179
|
-
}
|
|
5180
|
-
function buildUpdateWhereCondition(condition, vendor) {
|
|
5154
|
+
function buildWhereCondition(condition, vendor) {
|
|
5181
5155
|
validateIdentifier(condition.column, "WHERE column");
|
|
5182
5156
|
const column = sql.raw(quoteIdentifier(condition.column, vendor));
|
|
5183
5157
|
switch (condition.operator) {
|
|
@@ -5226,12 +5200,12 @@ function buildUpdateWhereCondition(condition, vendor) {
|
|
|
5226
5200
|
if (!Array.isArray(condition.value) || condition.value.length === 0) {
|
|
5227
5201
|
throw new Error(`IN operator requires a non-empty array value for column ${condition.column}`);
|
|
5228
5202
|
}
|
|
5229
|
-
return sql`${column} IN (${sql.join(condition.value.map((
|
|
5203
|
+
return sql`${column} IN (${sql.join(condition.value.map((value) => sql`${value}`), sql.raw(", "))})`;
|
|
5230
5204
|
case "notIn":
|
|
5231
5205
|
if (!Array.isArray(condition.value) || condition.value.length === 0) {
|
|
5232
5206
|
throw new Error(`NOT IN operator requires a non-empty array value for column ${condition.column}`);
|
|
5233
5207
|
}
|
|
5234
|
-
return sql`${column} NOT IN (${sql.join(condition.value.map((
|
|
5208
|
+
return sql`${column} NOT IN (${sql.join(condition.value.map((value) => sql`${value}`), sql.raw(", "))})`;
|
|
5235
5209
|
case "isNull":
|
|
5236
5210
|
if (condition.value !== void 0) {
|
|
5237
5211
|
throw new Error(`IS NULL operator must not include value for column ${condition.column}`);
|
|
@@ -5244,6 +5218,24 @@ function buildUpdateWhereCondition(condition, vendor) {
|
|
|
5244
5218
|
return sql`${column} IS NOT NULL`;
|
|
5245
5219
|
}
|
|
5246
5220
|
}
|
|
5221
|
+
|
|
5222
|
+
// src/data/relational/query/query-builder-mutation.ts
|
|
5223
|
+
function normalizeUpdateData(data) {
|
|
5224
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
5225
|
+
throw new Error("Update data must be an object");
|
|
5226
|
+
}
|
|
5227
|
+
const keys = Object.keys(data);
|
|
5228
|
+
if (keys.length === 0) {
|
|
5229
|
+
throw new Error("Update data must not be empty");
|
|
5230
|
+
}
|
|
5231
|
+
keys.forEach((column) => {
|
|
5232
|
+
validateIdentifier(column, "Update column");
|
|
5233
|
+
if (data[column] === void 0) {
|
|
5234
|
+
throw new Error(`Update value for column "${column}" must not be undefined`);
|
|
5235
|
+
}
|
|
5236
|
+
});
|
|
5237
|
+
return data;
|
|
5238
|
+
}
|
|
5247
5239
|
function buildUpdateQuery(input) {
|
|
5248
5240
|
validateQualifiedIdentifier(input.table, "Table name");
|
|
5249
5241
|
const normalizedData = normalizeUpdateData(input.data);
|
|
@@ -5252,7 +5244,7 @@ function buildUpdateQuery(input) {
|
|
|
5252
5244
|
return sql`${quotedColumn} = ${value}`;
|
|
5253
5245
|
});
|
|
5254
5246
|
const whereConditions = (input.where ?? []).map(
|
|
5255
|
-
(condition) =>
|
|
5247
|
+
(condition) => buildWhereCondition(condition, input.vendor)
|
|
5256
5248
|
);
|
|
5257
5249
|
if (input.optimisticLock) {
|
|
5258
5250
|
validateIdentifier(input.optimisticLock.column, "Optimistic lock column");
|
|
@@ -5263,7 +5255,9 @@ function buildUpdateQuery(input) {
|
|
|
5263
5255
|
whereConditions.push(sql`${lockColumn} = ${input.optimisticLock.expectedValue}`);
|
|
5264
5256
|
}
|
|
5265
5257
|
if (whereConditions.length === 0 && !input.allowFullTableUpdate) {
|
|
5266
|
-
throw new Error(
|
|
5258
|
+
throw new Error(
|
|
5259
|
+
"WHERE conditions are required for UPDATE queries. Set allowFullTableUpdate=true to override."
|
|
5260
|
+
);
|
|
5267
5261
|
}
|
|
5268
5262
|
let query = sql.join(
|
|
5269
5263
|
[
|
|
@@ -5273,7 +5267,10 @@ function buildUpdateQuery(input) {
|
|
|
5273
5267
|
sql.raw("")
|
|
5274
5268
|
);
|
|
5275
5269
|
if (whereConditions.length > 0) {
|
|
5276
|
-
query = sql.join(
|
|
5270
|
+
query = sql.join(
|
|
5271
|
+
[query, sql.raw(" WHERE "), sql.join(whereConditions, sql.raw(" AND "))],
|
|
5272
|
+
sql.raw("")
|
|
5273
|
+
);
|
|
5277
5274
|
}
|
|
5278
5275
|
return {
|
|
5279
5276
|
query,
|
|
@@ -5284,10 +5281,12 @@ function buildUpdateQuery(input) {
|
|
|
5284
5281
|
function buildDeleteQuery(input) {
|
|
5285
5282
|
validateQualifiedIdentifier(input.table, "Table name");
|
|
5286
5283
|
const whereConditions = (input.where ?? []).map(
|
|
5287
|
-
(condition) =>
|
|
5284
|
+
(condition) => buildWhereCondition(condition, input.vendor)
|
|
5288
5285
|
);
|
|
5289
5286
|
if (whereConditions.length === 0 && !input.allowFullTableDelete) {
|
|
5290
|
-
throw new Error(
|
|
5287
|
+
throw new Error(
|
|
5288
|
+
"WHERE conditions are required for DELETE queries. Set allowFullTableDelete=true to override."
|
|
5289
|
+
);
|
|
5291
5290
|
}
|
|
5292
5291
|
const softDeleteColumn = input.softDelete?.column ?? "deleted_at";
|
|
5293
5292
|
const softDeleteValue = input.softDelete?.value ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -5306,7 +5305,10 @@ function buildDeleteQuery(input) {
|
|
|
5306
5305
|
query = sql.raw(`DELETE FROM ${quoteQualifiedIdentifier(input.table, input.vendor)}`);
|
|
5307
5306
|
}
|
|
5308
5307
|
if (whereConditions.length > 0) {
|
|
5309
|
-
query = sql.join(
|
|
5308
|
+
query = sql.join(
|
|
5309
|
+
[query, sql.raw(" WHERE "), sql.join(whereConditions, sql.raw(" AND "))],
|
|
5310
|
+
sql.raw("")
|
|
5311
|
+
);
|
|
5310
5312
|
}
|
|
5311
5313
|
return {
|
|
5312
5314
|
query,
|
|
@@ -5315,73 +5317,6 @@ function buildDeleteQuery(input) {
|
|
|
5315
5317
|
softDeleteColumn: input.softDelete ? softDeleteColumn : void 0
|
|
5316
5318
|
};
|
|
5317
5319
|
}
|
|
5318
|
-
function buildSelectWhereCondition(condition, vendor) {
|
|
5319
|
-
validateIdentifier(condition.column, "WHERE column");
|
|
5320
|
-
const column = sql.raw(quoteIdentifier(condition.column, vendor));
|
|
5321
|
-
switch (condition.operator) {
|
|
5322
|
-
case "eq":
|
|
5323
|
-
if (condition.value === void 0) {
|
|
5324
|
-
throw new Error(`EQ operator requires a value for column ${condition.column}`);
|
|
5325
|
-
}
|
|
5326
|
-
if (condition.value === null) {
|
|
5327
|
-
throw new Error("null is only allowed with isNull/isNotNull operators");
|
|
5328
|
-
}
|
|
5329
|
-
return sql`${column} = ${condition.value}`;
|
|
5330
|
-
case "ne":
|
|
5331
|
-
if (condition.value === void 0) {
|
|
5332
|
-
throw new Error(`NE operator requires a value for column ${condition.column}`);
|
|
5333
|
-
}
|
|
5334
|
-
if (condition.value === null) {
|
|
5335
|
-
throw new Error("null is only allowed with isNull/isNotNull operators");
|
|
5336
|
-
}
|
|
5337
|
-
return sql`${column} != ${condition.value}`;
|
|
5338
|
-
case "gt":
|
|
5339
|
-
if (typeof condition.value !== "string" && typeof condition.value !== "number") {
|
|
5340
|
-
throw new Error(`GT operator requires a string or number value for column ${condition.column}`);
|
|
5341
|
-
}
|
|
5342
|
-
return sql`${column} > ${condition.value}`;
|
|
5343
|
-
case "lt":
|
|
5344
|
-
if (typeof condition.value !== "string" && typeof condition.value !== "number") {
|
|
5345
|
-
throw new Error(`LT operator requires a string or number value for column ${condition.column}`);
|
|
5346
|
-
}
|
|
5347
|
-
return sql`${column} < ${condition.value}`;
|
|
5348
|
-
case "gte":
|
|
5349
|
-
if (typeof condition.value !== "string" && typeof condition.value !== "number") {
|
|
5350
|
-
throw new Error(`GTE operator requires a string or number value for column ${condition.column}`);
|
|
5351
|
-
}
|
|
5352
|
-
return sql`${column} >= ${condition.value}`;
|
|
5353
|
-
case "lte":
|
|
5354
|
-
if (typeof condition.value !== "string" && typeof condition.value !== "number") {
|
|
5355
|
-
throw new Error(`LTE operator requires a string or number value for column ${condition.column}`);
|
|
5356
|
-
}
|
|
5357
|
-
return sql`${column} <= ${condition.value}`;
|
|
5358
|
-
case "like":
|
|
5359
|
-
if (typeof condition.value !== "string") {
|
|
5360
|
-
throw new Error(`LIKE operator requires a string value for column ${condition.column}`);
|
|
5361
|
-
}
|
|
5362
|
-
return sql`${column} LIKE ${condition.value}`;
|
|
5363
|
-
case "in":
|
|
5364
|
-
if (!Array.isArray(condition.value) || condition.value.length === 0) {
|
|
5365
|
-
throw new Error(`IN operator requires a non-empty array value for column ${condition.column}`);
|
|
5366
|
-
}
|
|
5367
|
-
return sql`${column} IN (${sql.join(condition.value.map((v) => sql`${v}`), sql.raw(", "))})`;
|
|
5368
|
-
case "notIn":
|
|
5369
|
-
if (!Array.isArray(condition.value) || condition.value.length === 0) {
|
|
5370
|
-
throw new Error(`NOT IN operator requires a non-empty array value for column ${condition.column}`);
|
|
5371
|
-
}
|
|
5372
|
-
return sql`${column} NOT IN (${sql.join(condition.value.map((v) => sql`${v}`), sql.raw(", "))})`;
|
|
5373
|
-
case "isNull":
|
|
5374
|
-
if (condition.value !== void 0) {
|
|
5375
|
-
throw new Error(`IS NULL operator must not include value for column ${condition.column}`);
|
|
5376
|
-
}
|
|
5377
|
-
return sql`${column} IS NULL`;
|
|
5378
|
-
case "isNotNull":
|
|
5379
|
-
if (condition.value !== void 0) {
|
|
5380
|
-
throw new Error(`IS NOT NULL operator must not include value for column ${condition.column}`);
|
|
5381
|
-
}
|
|
5382
|
-
return sql`${column} IS NOT NULL`;
|
|
5383
|
-
}
|
|
5384
|
-
}
|
|
5385
5320
|
function buildSelectQuery(input) {
|
|
5386
5321
|
validateQualifiedIdentifier(input.table, "Table name");
|
|
5387
5322
|
let query = sql.raw("SELECT ");
|
|
@@ -5399,7 +5334,9 @@ function buildSelectQuery(input) {
|
|
|
5399
5334
|
sql.raw("")
|
|
5400
5335
|
);
|
|
5401
5336
|
if (input.where && input.where.length > 0) {
|
|
5402
|
-
const whereConditions = input.where.map(
|
|
5337
|
+
const whereConditions = input.where.map(
|
|
5338
|
+
(condition) => buildWhereCondition(condition, input.vendor)
|
|
5339
|
+
);
|
|
5403
5340
|
query = sql.join(
|
|
5404
5341
|
[query, sql.raw(" WHERE "), sql.join(whereConditions, sql.raw(" AND "))],
|
|
5405
5342
|
sql.raw("")
|