@dbsp/adapter-pgsql 3.4.0 → 3.5.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/dist/index.d.ts CHANGED
@@ -960,6 +960,10 @@ declare class PgsqlTransactionTimeoutError extends Error {
960
960
  /**
961
961
  * Options for PgsqlAdapter.
962
962
  */
963
+ interface PgsqlPreparedStatementsOptions {
964
+ /** Maximum distinct compiled statements admitted per executor (default: 500). */
965
+ readonly maxStatements?: number;
966
+ }
963
967
  interface PgsqlAdapterOptions {
964
968
  /** Schema name for multi-tenant queries */
965
969
  readonly schemaName?: string;
@@ -978,6 +982,11 @@ interface PgsqlAdapterOptions {
978
982
  readonly defaultPkColumnName?: string;
979
983
  /** Convention for deriving FK column names: (tableName, pkName) => fkColumnName */
980
984
  readonly deriveFkColumnName?: FkColumnDerivation;
985
+ /**
986
+ * Opt in to node-postgres named prepared statements for compiled queries with
987
+ * parameters. `true` uses the default statement cap; an object overrides it.
988
+ */
989
+ readonly preparedStatements?: boolean | PgsqlPreparedStatementsOptions;
981
990
  }
982
991
  interface PgsqlPoolAdapterOptions extends PgsqlAdapterOptions {
983
992
  readonly borrowedClient?: false;
@@ -1042,6 +1051,8 @@ declare class PgsqlAdapter<DB = unknown> implements Adapter<DB> {
1042
1051
  private readonly naming;
1043
1052
  private readonly model;
1044
1053
  private readonly logger;
1054
+ private readonly preparedStatements;
1055
+ private readonly preparedStatementRegistry;
1045
1056
  private readonly _capabilities;
1046
1057
  private readonly defaultPk;
1047
1058
  private readonly deriveFk;
@@ -3585,4 +3596,4 @@ declare function escapeDiagnosticText(value: string): string;
3585
3596
  */
3586
3597
  declare function validateSqlExpression(sql: string, context: string): void;
3587
3598
 
3588
- export { ADD_CHECK_RULE_ID, ALTER_AUTHORITY_OBSERVATION, ALTER_COLUMN_SET_NOT_NULL_OPERATION_KIND, ALTER_TYPE_ADD_VALUE_OPERATION_KIND, ATTACH_LOGICAL_IDENTITY_OPERATION_KIND, type AddedEnumValue, type AlterColumnSetNotNullPayload, type AlterTypeAddValuePayload, type AttachLogicalIdentityPayload, type BatchValuesJoinDecision, COLUMN_EXISTS_OBSERVATION, CamelCaseNamingPlugin, type CanonicalizeCheckConstraintsOptions, type ChangeKind, CheckConstraintCanonicalizationError, type CheckConstraintCanonicalizationWarning, CheckConstraintNewEnumValueError, ColumnDefaultCanonicalizationError, type ColumnDefaultCanonicalizationWarning, type ComparePgsqlDatabaseSchemaOptions, type CompareSchemataOptions, type CompiledResult, type CompilerContext, type CompilerOptions, type CompilerState, type ConflictAction, type ConflictTarget, type CursorHoldOption, type CursorOptions, type CursorScrollOption, DBSP_LOGICAL_IDENTITY_TABLE, DBSP_META_SCHEMA, DBSP_TRANSITION_JOURNAL_TABLE, DBSP_TRANSITION_RUN_PLAN_TABLE, DBSP_TRANSITION_RUN_TABLE, DEFAULT_PK_COLUMN, DEFAULT_REDACTION_PATTERNS, type Decision, type DeleteConfig, type DetectedHierarchy, type DiffSummary, ENGINE_VERSION_OBSERVATION, ENUM_ADD_VALUE_RULE_ID, ENUM_LABEL_VISIBLE_OBSERVATION, ENUM_TYPE_EXISTS_OBSERVATION, type EnumAddValueMatch, type EnumAddValueRuleOptions, type ExplainFormat, type ExplainOptions, type ExplainPlan, ExpressionCanonicalizationUnavailableError, type ExpressionCanonicalizationWarning, type ExpressionHandler, type FetchDirection, type FetchOptions, type FkColumnDerivation, type GenerateDDLOptions, type IdentityAdoptionAsserter, IdentityNamingPlugin, type IncludeHandler, type IncludeResult, type IndexCapabilityContext, IndexFeatureUnsupportedError, type IndexRenderSpec, type InsertConfig, type IntrospectedModelIR, type IntrospectionOptions, InvalidIdentifierError, type JoinDecision, LOGICAL_IDENTITY_ADOPTION_RULE_ID, LOGICAL_IDENTITY_CARRIER_OBSERVATION, type LeafCompileFn, type LogicalIdentityAdoptionMatch, type LogicalIdentityAdoptionRuleOptions, MANUAL_SQL_OPERATION_KIND, type ManualSqlPayload, type MigrationRecord, type MigrationSQLOptions, NO_NULLS_GUARD, type NamingPlugin, NonConvergentSchemaDiffError, type NonConvergentSchemaDiffSurface, PG_INTROSPECTION_ARTIFACT, PG_OPERATION_PACK_ARTIFACT, PG_RULE_PACK_ARTIFACT, type ParamRefValidationResult, type ParsedMigrationFile, type PgAdvisoryLockKey, type PgAdvisoryLockResult, PgExecutionContractDerivationError, type PgTransitionClientLease, type PgTransitionPackOptions, PgsqlAdapter, type PgsqlAdapterOptions, PgsqlAdvisoryLockOptionsError, type PgsqlBorrowedClientAdapterOptions, PgsqlPinnedConnectionAbortSignalError, type PgsqlPoolAdapterOptions, PgsqlRawSqlTransactionControlError, PgsqlTransactionAbortSignalError, PgsqlTransactionAbortedCommitError, PgsqlTransactionAbortedError, PgsqlTransactionOptionsError, PgsqlTransactionTimeoutError, PlanCompiler, type PlanDecision, type PrecompiledJoinDecision, type RedactionConfig, type RedactionPattern, type RollbackOnlyPgsqlScope, SET_NOT_NULL_RULE_ID, type SchemaChange, type SchemaDiff, type SchemaScopeOptions, type SetNotNullMatch, type SetNotNullRuleOptions, type SetOperationResult, type SimplifiedPlanReport, type StreamConfig, type UpdateConfig, type UpsertConfig, type WhereDispatcher, type WhereHandler, acquirePgTransitionClient, assertCreateIndexSupported, assertCreateIndexesSupported, assertNoRepeatedExpressionSurfaceDrift, bm25Search, booleanSearch, boost, buildCloseCursor, buildDeclareCursor, buildExplain, buildExplainAnalyzeJson, buildExplainPlan, buildExplainVerbose, buildFetch, buildFetchAll, buildFetchFirst, buildFetchForward, buildFetchNext, buildOnConflictClause, buildStreamingStatements, camelCaseNaming, canGenerateCreateIndex, canonicalizeCheckConstraints, collectAndValidateParamRefs, comparePgsqlDatabaseSchema, compareSchemata, compileDelete, compileInsert, compileMutation, compilePlan, compileSetOperation, compileUpdate, compileUpsert, conditionalUpdate, cosineDistance, createAlterColumnSetNotNullOperationRuntime, createAlterTypeAddValueOperationRuntime, createAnyExpr, createAttachLogicalIdentityOperationRuntime, createEnumAddValueRule, createEqualityExpr, createLeafCompileFn, createLogicalIdentityAdoptionRule, createManualSqlOperationRuntime, createParamRef, createPgExecutionContract, createPgObservationIssuer, createPgTransitionLessor, createPgTransitionPack, createPgTransitionRunPersister, createPgsqlAdapter, createPgsqlCompileOnlyAdapter, createSetNotNullRule, createTypeCastParamRef, defaultFkDerivation, derivePostgresqlCapabilitiesForVersion, ensureMigrationsTable, ensureTransitionJournal, escapeDiagnosticText, evaluatePgExecutionContract, excludedRef, executePgObservationFromLessor, generateCreateIndex, generateCursorName, generateDDL, generateDownSQL, generateMigrationFile, generateMigrationSQL, generateSeries, getAppliedMigrations, getNamingPluginForDbCasing, getNextSchemaVersion, getRowEstimates, getTotalExecutionTime, identityNaming, innerProduct, introspect, isBatchValuesJoinDecision, isDestructiveDown, isJoinDecision, isMigrationApplied, isPrecompiledJoinDecision, isReservedKeyword, l2Distance, mapColumnType, mapOnDeleteAction, nextval, normalizeManualSqlPayload, parse, parseExplainJson, parseMigrationFile, pgTargetIdentityMismatch, rawDistance, readPgExecutionTargetFromClient, readPgObservationContextFromLessor, readTransitionJournal, recordMigration, redactParams, removeMigrationRecord, renderAlterColumnSetNotNullSql, renderAlterTypeAddValueSql, renderAttachLogicalIdentityLockSql, renderCreateDbspMetaSchemaSql, renderCreateIndex, renderCreateLogicalIdentityIndexesSql, renderCreateLogicalIdentitySideTableSql, renderCreateTransitionJournalTableSql, renderCreateTransitionRunPlanTableSql, renderCreateTransitionRunTableSql, renderInsertLogicalIdentitySql, renderNoNullsCheckSql, renderSetNotNullLockSql, resolveLogicalName, sanitizeForDisplay, score, validateIdentifier, validateIdentifiers, validateParamRef, validateQualifiedIdentifier, validateSqlExpression, vectorDims, withMigrationLock };
3599
+ export { ADD_CHECK_RULE_ID, ALTER_AUTHORITY_OBSERVATION, ALTER_COLUMN_SET_NOT_NULL_OPERATION_KIND, ALTER_TYPE_ADD_VALUE_OPERATION_KIND, ATTACH_LOGICAL_IDENTITY_OPERATION_KIND, type AddedEnumValue, type AlterColumnSetNotNullPayload, type AlterTypeAddValuePayload, type AttachLogicalIdentityPayload, type BatchValuesJoinDecision, COLUMN_EXISTS_OBSERVATION, CamelCaseNamingPlugin, type CanonicalizeCheckConstraintsOptions, type ChangeKind, CheckConstraintCanonicalizationError, type CheckConstraintCanonicalizationWarning, CheckConstraintNewEnumValueError, ColumnDefaultCanonicalizationError, type ColumnDefaultCanonicalizationWarning, type ComparePgsqlDatabaseSchemaOptions, type CompareSchemataOptions, type CompiledResult, type CompilerContext, type CompilerOptions, type CompilerState, type ConflictAction, type ConflictTarget, type CursorHoldOption, type CursorOptions, type CursorScrollOption, DBSP_LOGICAL_IDENTITY_TABLE, DBSP_META_SCHEMA, DBSP_TRANSITION_JOURNAL_TABLE, DBSP_TRANSITION_RUN_PLAN_TABLE, DBSP_TRANSITION_RUN_TABLE, DEFAULT_PK_COLUMN, DEFAULT_REDACTION_PATTERNS, type Decision, type DeleteConfig, type DetectedHierarchy, type DiffSummary, ENGINE_VERSION_OBSERVATION, ENUM_ADD_VALUE_RULE_ID, ENUM_LABEL_VISIBLE_OBSERVATION, ENUM_TYPE_EXISTS_OBSERVATION, type EnumAddValueMatch, type EnumAddValueRuleOptions, type ExplainFormat, type ExplainOptions, type ExplainPlan, ExpressionCanonicalizationUnavailableError, type ExpressionCanonicalizationWarning, type ExpressionHandler, type FetchDirection, type FetchOptions, type FkColumnDerivation, type GenerateDDLOptions, type IdentityAdoptionAsserter, IdentityNamingPlugin, type IncludeHandler, type IncludeResult, type IndexCapabilityContext, IndexFeatureUnsupportedError, type IndexRenderSpec, type InsertConfig, type IntrospectedModelIR, type IntrospectionOptions, InvalidIdentifierError, type JoinDecision, LOGICAL_IDENTITY_ADOPTION_RULE_ID, LOGICAL_IDENTITY_CARRIER_OBSERVATION, type LeafCompileFn, type LogicalIdentityAdoptionMatch, type LogicalIdentityAdoptionRuleOptions, MANUAL_SQL_OPERATION_KIND, type ManualSqlPayload, type MigrationRecord, type MigrationSQLOptions, NO_NULLS_GUARD, type NamingPlugin, NonConvergentSchemaDiffError, type NonConvergentSchemaDiffSurface, PG_INTROSPECTION_ARTIFACT, PG_OPERATION_PACK_ARTIFACT, PG_RULE_PACK_ARTIFACT, type ParamRefValidationResult, type ParsedMigrationFile, type PgAdvisoryLockKey, type PgAdvisoryLockResult, PgExecutionContractDerivationError, type PgTransitionClientLease, type PgTransitionPackOptions, PgsqlAdapter, type PgsqlAdapterOptions, PgsqlAdvisoryLockOptionsError, type PgsqlBorrowedClientAdapterOptions, PgsqlPinnedConnectionAbortSignalError, type PgsqlPoolAdapterOptions, type PgsqlPreparedStatementsOptions, PgsqlRawSqlTransactionControlError, PgsqlTransactionAbortSignalError, PgsqlTransactionAbortedCommitError, PgsqlTransactionAbortedError, PgsqlTransactionOptionsError, PgsqlTransactionTimeoutError, PlanCompiler, type PlanDecision, type PrecompiledJoinDecision, type RedactionConfig, type RedactionPattern, type RollbackOnlyPgsqlScope, SET_NOT_NULL_RULE_ID, type SchemaChange, type SchemaDiff, type SchemaScopeOptions, type SetNotNullMatch, type SetNotNullRuleOptions, type SetOperationResult, type SimplifiedPlanReport, type StreamConfig, type UpdateConfig, type UpsertConfig, type WhereDispatcher, type WhereHandler, acquirePgTransitionClient, assertCreateIndexSupported, assertCreateIndexesSupported, assertNoRepeatedExpressionSurfaceDrift, bm25Search, booleanSearch, boost, buildCloseCursor, buildDeclareCursor, buildExplain, buildExplainAnalyzeJson, buildExplainPlan, buildExplainVerbose, buildFetch, buildFetchAll, buildFetchFirst, buildFetchForward, buildFetchNext, buildOnConflictClause, buildStreamingStatements, camelCaseNaming, canGenerateCreateIndex, canonicalizeCheckConstraints, collectAndValidateParamRefs, comparePgsqlDatabaseSchema, compareSchemata, compileDelete, compileInsert, compileMutation, compilePlan, compileSetOperation, compileUpdate, compileUpsert, conditionalUpdate, cosineDistance, createAlterColumnSetNotNullOperationRuntime, createAlterTypeAddValueOperationRuntime, createAnyExpr, createAttachLogicalIdentityOperationRuntime, createEnumAddValueRule, createEqualityExpr, createLeafCompileFn, createLogicalIdentityAdoptionRule, createManualSqlOperationRuntime, createParamRef, createPgExecutionContract, createPgObservationIssuer, createPgTransitionLessor, createPgTransitionPack, createPgTransitionRunPersister, createPgsqlAdapter, createPgsqlCompileOnlyAdapter, createSetNotNullRule, createTypeCastParamRef, defaultFkDerivation, derivePostgresqlCapabilitiesForVersion, ensureMigrationsTable, ensureTransitionJournal, escapeDiagnosticText, evaluatePgExecutionContract, excludedRef, executePgObservationFromLessor, generateCreateIndex, generateCursorName, generateDDL, generateDownSQL, generateMigrationFile, generateMigrationSQL, generateSeries, getAppliedMigrations, getNamingPluginForDbCasing, getNextSchemaVersion, getRowEstimates, getTotalExecutionTime, identityNaming, innerProduct, introspect, isBatchValuesJoinDecision, isDestructiveDown, isJoinDecision, isMigrationApplied, isPrecompiledJoinDecision, isReservedKeyword, l2Distance, mapColumnType, mapOnDeleteAction, nextval, normalizeManualSqlPayload, parse, parseExplainJson, parseMigrationFile, pgTargetIdentityMismatch, rawDistance, readPgExecutionTargetFromClient, readPgObservationContextFromLessor, readTransitionJournal, recordMigration, redactParams, removeMigrationRecord, renderAlterColumnSetNotNullSql, renderAlterTypeAddValueSql, renderAttachLogicalIdentityLockSql, renderCreateDbspMetaSchemaSql, renderCreateIndex, renderCreateLogicalIdentityIndexesSql, renderCreateLogicalIdentitySideTableSql, renderCreateTransitionJournalTableSql, renderCreateTransitionRunPlanTableSql, renderCreateTransitionRunTableSql, renderInsertLogicalIdentitySql, renderNoNullsCheckSql, renderSetNotNullLockSql, resolveLogicalName, sanitizeForDisplay, score, validateIdentifier, validateIdentifiers, validateParamRef, validateQualifiedIdentifier, validateSqlExpression, vectorDims, withMigrationLock };
package/dist/index.js CHANGED
@@ -23010,6 +23010,123 @@ function matchGlob(pattern, value) {
23010
23010
 
23011
23011
  // src/pgsql-adapter.ts
23012
23012
  init_naming_plugin();
23013
+
23014
+ // src/prepared-statements.ts
23015
+ import { createHash as createHash2 } from "crypto";
23016
+ var DEFAULT_MAX_PREPARED_STATEMENTS = 500;
23017
+ var PREPARED_STATEMENT_NAMESPACE = "dbsp_ps_";
23018
+ function derivePreparedStatementFingerprint(sql) {
23019
+ return createHash2("sha256").update(sql).digest("hex");
23020
+ }
23021
+ function derivePreparedStatementName(sql) {
23022
+ return `${PREPARED_STATEMENT_NAMESPACE}${derivePreparedStatementFingerprint(sql).slice(0, 32)}`;
23023
+ }
23024
+ var PreparedStatementRegistry = class {
23025
+ constructor(maxStatements, hashName = derivePreparedStatementName) {
23026
+ this.maxStatements = maxStatements;
23027
+ this.hashName = hashName;
23028
+ }
23029
+ maxStatements;
23030
+ hashName;
23031
+ candidates = /* @__PURE__ */ new Set();
23032
+ namesByFingerprint = /* @__PURE__ */ new Map();
23033
+ fingerprintsByName = /* @__PURE__ */ new Map();
23034
+ pendingByFingerprint = /* @__PURE__ */ new Map();
23035
+ collisionRejectedFingerprints = /* @__PURE__ */ new Set();
23036
+ nextGeneration = 0;
23037
+ /**
23038
+ * Reserves a name only from the second sighting onward. Call confirm after
23039
+ * successful named execution, or abort when that execution fails.
23040
+ */
23041
+ admit(sql) {
23042
+ const fingerprint = derivePreparedStatementFingerprint(sql);
23043
+ if (this.collisionRejectedFingerprints.has(fingerprint)) return void 0;
23044
+ const knownName = this.namesByFingerprint.get(fingerprint);
23045
+ if (knownName !== void 0) return { name: knownName };
23046
+ const pending = this.pendingByFingerprint.get(fingerprint);
23047
+ if (pending !== void 0) {
23048
+ return this.reserveAttempt(fingerprint, pending);
23049
+ }
23050
+ if (!this.candidates.delete(fingerprint)) {
23051
+ if (this.atCapacity()) return void 0;
23052
+ if (this.candidates.size >= this.maxStatements) {
23053
+ const oldest = this.candidates.values().next().value;
23054
+ if (oldest !== void 0) this.candidates.delete(oldest);
23055
+ }
23056
+ this.candidates.add(fingerprint);
23057
+ return void 0;
23058
+ }
23059
+ if (this.atCapacity()) return void 0;
23060
+ const name = this.hashName(sql);
23061
+ const existingFingerprint = this.fingerprintsByName.get(name);
23062
+ if (existingFingerprint !== void 0 && existingFingerprint !== fingerprint) {
23063
+ this.rejectHashCollision(fingerprint);
23064
+ return void 0;
23065
+ }
23066
+ this.fingerprintsByName.set(name, fingerprint);
23067
+ const reservation = this.reserveAttempt(fingerprint, {
23068
+ name,
23069
+ generations: /* @__PURE__ */ new Set()
23070
+ });
23071
+ if (this.atCapacity()) this.candidates.clear();
23072
+ return reservation;
23073
+ }
23074
+ /** Confirms a reservation only after PostgreSQL accepted its named execution. */
23075
+ confirm(reservation) {
23076
+ const pending = this.pendingByFingerprint.get(reservation.fingerprint);
23077
+ if (pending === void 0 || pending.name !== reservation.name || !pending.generations.has(reservation.generation))
23078
+ return;
23079
+ this.pendingByFingerprint.delete(reservation.fingerprint);
23080
+ this.namesByFingerprint.set(reservation.fingerprint, reservation.name);
23081
+ }
23082
+ /** Aborts only this still-pending attempt, never a later confirmation. */
23083
+ abort(reservation) {
23084
+ const pending = this.pendingByFingerprint.get(reservation.fingerprint);
23085
+ if (pending === void 0 || pending.name !== reservation.name || !pending.generations.delete(reservation.generation))
23086
+ return;
23087
+ if (pending.generations.size !== 0) return;
23088
+ this.pendingByFingerprint.delete(reservation.fingerprint);
23089
+ if (this.fingerprintsByName.get(reservation.name) === reservation.fingerprint) {
23090
+ this.fingerprintsByName.delete(reservation.name);
23091
+ }
23092
+ }
23093
+ atCapacity() {
23094
+ return this.namesByFingerprint.size + this.pendingByFingerprint.size >= this.maxStatements;
23095
+ }
23096
+ reserveAttempt(fingerprint, pending) {
23097
+ this.pendingByFingerprint.set(fingerprint, pending);
23098
+ const reservation = {
23099
+ fingerprint,
23100
+ name: pending.name,
23101
+ generation: this.nextGeneration++
23102
+ };
23103
+ pending.generations.add(reservation.generation);
23104
+ return { name: pending.name, reservation };
23105
+ }
23106
+ /** Keeps a colliding full fingerprint unnamed within a bounded registry. */
23107
+ rejectHashCollision(fingerprint) {
23108
+ if (this.collisionRejectedFingerprints.size >= this.maxStatements) {
23109
+ const oldest = this.collisionRejectedFingerprints.values().next().value;
23110
+ if (oldest !== void 0)
23111
+ this.collisionRejectedFingerprints.delete(oldest);
23112
+ }
23113
+ this.collisionRejectedFingerprints.add(fingerprint);
23114
+ this.candidates.delete(fingerprint);
23115
+ }
23116
+ };
23117
+ function normalizeMaxPreparedStatements(maxStatements) {
23118
+ const resolved = maxStatements === void 0 ? DEFAULT_MAX_PREPARED_STATEMENTS : maxStatements;
23119
+ if (!Number.isSafeInteger(resolved))
23120
+ throw new Error("preparedStatements.maxStatements must be a safe integer.");
23121
+ const normalized = resolved;
23122
+ if (normalized < 1)
23123
+ throw new Error(
23124
+ "preparedStatements.maxStatements must be greater than zero."
23125
+ );
23126
+ return normalized;
23127
+ }
23128
+
23129
+ // src/pgsql-adapter.ts
23013
23130
  init_cte_compiler();
23014
23131
 
23015
23132
  // src/set-operation.ts
@@ -23264,6 +23381,8 @@ var pgsqlAdapterInternalOptionsKey = /* @__PURE__ */ Symbol(
23264
23381
  "dbsp-pgsql-adapter-internal-options"
23265
23382
  );
23266
23383
  var activeClientScopes = /* @__PURE__ */ new WeakMap();
23384
+ var preparedStatementRegistries = /* @__PURE__ */ new WeakMap();
23385
+ var preparedStatementQuarantines = /* @__PURE__ */ new WeakMap();
23267
23386
  var PgsqlRawSqlTransactionControlError = class extends Error {
23268
23387
  dbspRawSqlTransactionControl = true;
23269
23388
  constructor(cause) {
@@ -23334,6 +23453,57 @@ function poolClientTransactionOpen(client) {
23334
23453
  function isPgErrorWithCode(error, code) {
23335
23454
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
23336
23455
  }
23456
+ function isDriverLocalPreparedStatementNameCollision(error, attemptedName) {
23457
+ const message = `Prepared statements must be unique - '${attemptedName}' was used for a different statement`;
23458
+ return error instanceof Error && !("code" in error) && error.message === message;
23459
+ }
23460
+ function isVerifiedPreparedStatementInfrastructureError(error) {
23461
+ if (typeof error !== "object" || error === null) return false;
23462
+ const { code, routine } = error;
23463
+ return code === "0A000" && routine === "RevalidateCachedQuery" || code === "26000" && routine === "FetchPreparedStatement" || code === "42P05" && routine === "StorePreparedStatement";
23464
+ }
23465
+ function didNamedExecutionReachExecution(error) {
23466
+ if (typeof error !== "object" || error === null) return false;
23467
+ const { code, position } = error;
23468
+ return typeof code === "string" && /^[0-9A-Z]{5}$/.test(code) && position === void 0 && !isVerifiedPreparedStatementInfrastructureError(error);
23469
+ }
23470
+ function shouldAbortPreparedStatementReservation(error, executor, attemptedName) {
23471
+ if (typeof error === "object" && error !== null && "position" in error && error.position !== void 0)
23472
+ return true;
23473
+ if (isDriverLocalPreparedStatementNameCollision(error, attemptedName))
23474
+ return true;
23475
+ if (!isPoolClientLike(executor)) return false;
23476
+ if (didNamedExecutionReachExecution(error)) return false;
23477
+ return !(error instanceof Error && error.message.startsWith("Prepared statements must be unique - '"));
23478
+ }
23479
+ function isVerifiedClientWidePreparedStatementLoss(error) {
23480
+ if (typeof error !== "object" || error === null) return false;
23481
+ const { code, routine } = error;
23482
+ return code === "26000" && routine === "FetchPreparedStatement";
23483
+ }
23484
+ function isPreparedStatementQuarantined(client, sql) {
23485
+ const quarantine = preparedStatementQuarantines.get(client);
23486
+ return quarantine?.allStatements === true || quarantine?.fingerprints.has(derivePreparedStatementFingerprint(sql)) === true;
23487
+ }
23488
+ function hasPreparedStatementQuarantine(client) {
23489
+ return preparedStatementQuarantines.has(client);
23490
+ }
23491
+ function quarantinePreparedStatement(client, sql, clientWide) {
23492
+ const quarantine = preparedStatementQuarantines.get(client);
23493
+ if (quarantine !== void 0) {
23494
+ if (clientWide) {
23495
+ quarantine.allStatements = true;
23496
+ quarantine.fingerprints.clear();
23497
+ } else if (!quarantine.allStatements) {
23498
+ quarantine.fingerprints.add(derivePreparedStatementFingerprint(sql));
23499
+ }
23500
+ return;
23501
+ }
23502
+ preparedStatementQuarantines.set(client, {
23503
+ allStatements: clientWide,
23504
+ fingerprints: clientWide ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([derivePreparedStatementFingerprint(sql)])
23505
+ });
23506
+ }
23337
23507
  function describeTransactionOptionValue(value) {
23338
23508
  if (value === null) return "null";
23339
23509
  if (typeof value === "string") return `string ${JSON.stringify(value)}`;
@@ -24357,6 +24527,45 @@ function getDbspScopeTokenOption(options) {
24357
24527
  function getDbspScopeStateOption(options) {
24358
24528
  return isPgsqlAdapterInternalOptions(options) ? options.dbspScopeState : void 0;
24359
24529
  }
24530
+ function getPreparedStatementRegistryOption(options) {
24531
+ return isPgsqlAdapterInternalOptions(options) ? options.preparedStatementRegistry : void 0;
24532
+ }
24533
+ function getOrCreatePreparedStatementRegistry(executor, maxStatements) {
24534
+ const configured = preparedStatementRegistries.get(executor);
24535
+ if (configured === void 0) {
24536
+ const registry = new PreparedStatementRegistry(maxStatements);
24537
+ preparedStatementRegistries.set(executor, { maxStatements, registry });
24538
+ return registry;
24539
+ }
24540
+ if (configured.maxStatements !== maxStatements) {
24541
+ const executorKind = isPoolClientLike(executor) ? "borrowed client" : "pool";
24542
+ throw new Error(
24543
+ `preparedStatements.maxStatements is configured ${executorKind}-wide: expected ${configured.maxStatements}, received ${maxStatements}.`
24544
+ );
24545
+ }
24546
+ return configured.registry;
24547
+ }
24548
+ function normalizePreparedStatements(value) {
24549
+ if (value === void 0 || value === false) return false;
24550
+ if (value === true) {
24551
+ return Object.freeze({
24552
+ maxStatements: normalizeMaxPreparedStatements(void 0)
24553
+ });
24554
+ }
24555
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
24556
+ throw new Error(
24557
+ "preparedStatements must be true, false, or a non-null options object."
24558
+ );
24559
+ }
24560
+ return Object.freeze({
24561
+ maxStatements: normalizeMaxPreparedStatements(
24562
+ value.maxStatements
24563
+ )
24564
+ });
24565
+ }
24566
+ function clonePreparedStatements(config) {
24567
+ return config === false ? false : { maxStatements: config.maxStatements };
24568
+ }
24360
24569
  function createPgsqlAdapterFromConstructionOptions(connection, options) {
24361
24570
  return new PgsqlAdapter(
24362
24571
  connection,
@@ -24378,6 +24587,8 @@ var PgsqlAdapter = class {
24378
24587
  naming;
24379
24588
  model;
24380
24589
  logger;
24590
+ preparedStatements;
24591
+ preparedStatementRegistry;
24381
24592
  _capabilities;
24382
24593
  defaultPk;
24383
24594
  deriveFk;
@@ -24424,6 +24635,15 @@ var PgsqlAdapter = class {
24424
24635
  this.naming = getNamingPluginForDbCasing(this._dbCasing);
24425
24636
  this.model = options?.model;
24426
24637
  this.logger = options?.logger;
24638
+ this.preparedStatements = normalizePreparedStatements(
24639
+ options?.preparedStatements
24640
+ );
24641
+ if (this.preparedStatements !== false) {
24642
+ const { maxStatements } = this.preparedStatements;
24643
+ this.preparedStatementRegistry = getPreparedStatementRegistryOption(options) ?? (this.pool !== void 0 ? getOrCreatePreparedStatementRegistry(this.pool, maxStatements) : this.client !== void 0 ? getOrCreatePreparedStatementRegistry(this.client, maxStatements) : void 0);
24644
+ } else {
24645
+ this.preparedStatementRegistry = void 0;
24646
+ }
24427
24647
  this.defaultPk = options?.defaultPkColumnName ?? DEFAULT_PK_COLUMN;
24428
24648
  this.deriveFk = options?.deriveFkColumnName ?? defaultFkDerivation;
24429
24649
  const supportsManagedTransactions = this.pool != null || this.client != null && this.managedTransactions;
@@ -24454,7 +24674,8 @@ var PgsqlAdapter = class {
24454
24674
  const scopeToken = overrides.dbspScopeToken ?? this.scopeToken;
24455
24675
  const scopeState = overrides.dbspScopeState ?? this.scopeState;
24456
24676
  const rollbackOnlyScope = overrides.rollbackOnlyScope ?? this.rollbackOnlyScope ? true : void 0;
24457
- const hasInternalOptions = adapterManagedTransaction === true || adapterManagedPinnedConnection === true || rollbackOnlyScope === true || scopeToken !== void 0 || scopeState !== void 0;
24677
+ const preparedStatementRegistry = this.preparedStatementRegistry;
24678
+ const hasInternalOptions = adapterManagedTransaction === true || adapterManagedPinnedConnection === true || rollbackOnlyScope === true || scopeToken !== void 0 || scopeState !== void 0 || preparedStatementRegistry !== void 0;
24458
24679
  return {
24459
24680
  ...hasInternalOptions && {
24460
24681
  [pgsqlAdapterInternalOptionsKey]: true
@@ -24463,6 +24684,9 @@ var PgsqlAdapter = class {
24463
24684
  ...this._dbCasing !== void 0 && { dbCasing: this._dbCasing },
24464
24685
  ...this.model !== void 0 && { model: this.model },
24465
24686
  ...this.logger !== void 0 && { logger: this.logger },
24687
+ ...this.preparedStatements !== false && {
24688
+ preparedStatements: clonePreparedStatements(this.preparedStatements)
24689
+ },
24466
24690
  defaultPkColumnName: this.defaultPk,
24467
24691
  deriveFkColumnName: this.deriveFk,
24468
24692
  ...this.borrowedClient && { borrowedClient: true },
@@ -24474,6 +24698,9 @@ var PgsqlAdapter = class {
24474
24698
  ...rollbackOnlyScope === true && { rollbackOnlyScope },
24475
24699
  ...scopeToken !== void 0 && { dbspScopeToken: scopeToken },
24476
24700
  ...scopeState !== void 0 && { dbspScopeState: scopeState },
24701
+ ...preparedStatementRegistry !== void 0 && {
24702
+ preparedStatementRegistry
24703
+ },
24477
24704
  ...overrides
24478
24705
  };
24479
24706
  }
@@ -25058,7 +25285,7 @@ var PgsqlAdapter = class {
25058
25285
  async executeWithMeta(query) {
25059
25286
  this.requireConnection("execute");
25060
25287
  const guardedQuery = guardCompiledQuery(query, "execute");
25061
- const result = await this.executeQueryProtectingOpenTransaction(guardedQuery.sql, guardedQuery.parameters);
25288
+ const result = await this.executeQueryProtectingOpenTransaction(guardedQuery.sql, guardedQuery.parameters, { prepareEligible: true });
25062
25289
  const metadata = queryResultMetadata(result);
25063
25290
  const rows = this.transformResultRows(result.rows, guardedQuery);
25064
25291
  return {
@@ -26559,11 +26786,11 @@ var PgsqlAdapter = class {
26559
26786
  }
26560
26787
  }
26561
26788
  releaseClient(client, error) {
26562
- if (error === void 0) {
26789
+ if (error === void 0 && !hasPreparedStatementQuarantine(client)) {
26563
26790
  client.release();
26564
26791
  return;
26565
26792
  }
26566
- client.release(error);
26793
+ client.release(error ?? true);
26567
26794
  }
26568
26795
  async rollbackAndReleaseSavepoint(client, savepointName, allowedScopeToken, scopeState, options) {
26569
26796
  const rolledBack = await this.rollbackSavepoint(
@@ -27260,7 +27487,8 @@ var PgsqlAdapter = class {
27260
27487
  const rawResult = await this.issueConnectionQuery(
27261
27488
  executor,
27262
27489
  sql,
27263
- parameters
27490
+ parameters,
27491
+ options.prepareEligible ?? false
27264
27492
  );
27265
27493
  if (options.inspectTransactionControl !== false) {
27266
27494
  await this.assertNoTransactionControlCommand(
@@ -27401,7 +27629,44 @@ var PgsqlAdapter = class {
27401
27629
  }
27402
27630
  return result;
27403
27631
  }
27404
- async issueConnectionQuery(executor, sql, parameters) {
27632
+ async issueConnectionQuery(executor, sql, parameters, prepareEligible) {
27633
+ if (this.preparedStatementRegistry !== void 0 && prepareEligible && parameters !== void 0 && parameters.length > 0) {
27634
+ if (isPoolClientLike(executor) && isPreparedStatementQuarantined(executor, sql)) {
27635
+ return executor.query(sql, [...parameters]);
27636
+ }
27637
+ const admission = this.preparedStatementRegistry.admit(sql);
27638
+ if (admission === void 0) {
27639
+ return executor.query(sql, [...parameters]);
27640
+ }
27641
+ try {
27642
+ const result = await executor.query({
27643
+ name: admission.name,
27644
+ text: sql,
27645
+ values: [...parameters]
27646
+ });
27647
+ if (admission.reservation !== void 0)
27648
+ this.preparedStatementRegistry.confirm(admission.reservation);
27649
+ return result;
27650
+ } catch (error) {
27651
+ if (admission.reservation !== void 0) {
27652
+ if (shouldAbortPreparedStatementReservation(
27653
+ error,
27654
+ executor,
27655
+ admission.name
27656
+ )) {
27657
+ this.preparedStatementRegistry.abort(admission.reservation);
27658
+ } else this.preparedStatementRegistry.confirm(admission.reservation);
27659
+ }
27660
+ if (isPoolClientLike(executor) && (isVerifiedPreparedStatementInfrastructureError(error) || isDriverLocalPreparedStatementNameCollision(error, admission.name))) {
27661
+ quarantinePreparedStatement(
27662
+ executor,
27663
+ sql,
27664
+ isVerifiedClientWidePreparedStatementLoss(error)
27665
+ );
27666
+ }
27667
+ throw error;
27668
+ }
27669
+ }
27405
27670
  if (parameters === void 0) {
27406
27671
  return executor.query(sql);
27407
27672
  }
@@ -30596,7 +30861,7 @@ function createPgObservationIssuer() {
30596
30861
  }
30597
30862
 
30598
30863
  // src/transition/operations/alter-column-set-not-null.ts
30599
- import { createHash as createHash2 } from "crypto";
30864
+ import { createHash as createHash3 } from "crypto";
30600
30865
  import { createEvidenceView } from "@dbsp/core";
30601
30866
  init_validate();
30602
30867
 
@@ -31869,7 +32134,7 @@ function operationPackSemanticsAssumption(payload, context) {
31869
32134
  };
31870
32135
  }
31871
32136
  function digest(value) {
31872
- return createHash2("sha256").update(stableJson(value)).digest("hex");
32137
+ return createHash3("sha256").update(stableJson(value)).digest("hex");
31873
32138
  }
31874
32139
  function fact(key, value) {
31875
32140
  return {
@@ -32541,7 +32806,7 @@ function createAlterColumnSetNotNullOperationRuntime() {
32541
32806
  }
32542
32807
 
32543
32808
  // src/transition/operations/alter-table-add-check.ts
32544
- import { createHash as createHash3 } from "crypto";
32809
+ import { createHash as createHash4 } from "crypto";
32545
32810
  init_validate();
32546
32811
  var GUARD_STATEMENT_TIMEOUT_MS2 = 5e3;
32547
32812
  var RENDERER_SESSION_REQUIREMENTS = [
@@ -32772,7 +33037,7 @@ function operationPackSemanticsAssumption2(payload, context) {
32772
33037
  };
32773
33038
  }
32774
33039
  function digest2(value) {
32775
- return createHash3("sha256").update(stableJson(value)).digest("hex");
33040
+ return createHash4("sha256").update(stableJson(value)).digest("hex");
32776
33041
  }
32777
33042
  function fact2(key, value) {
32778
33043
  return {
@@ -33246,7 +33511,7 @@ function createAlterTableAddCheckOperationRuntime() {
33246
33511
  }
33247
33512
 
33248
33513
  // src/transition/operations/alter-type-add-value.ts
33249
- import { createHash as createHash4 } from "crypto";
33514
+ import { createHash as createHash5 } from "crypto";
33250
33515
  init_validate();
33251
33516
  var MAX_PG_ENUM_LABEL_BYTES = 63;
33252
33517
  var RENDERER_SESSION_REQUIREMENTS2 = [
@@ -33414,7 +33679,7 @@ function operationPackSemanticsAssumption3(payload, context) {
33414
33679
  };
33415
33680
  }
33416
33681
  function digest3(value) {
33417
- return createHash4("sha256").update(stableJson(value)).digest("hex");
33682
+ return createHash5("sha256").update(stableJson(value)).digest("hex");
33418
33683
  }
33419
33684
  function fact3(key, value) {
33420
33685
  return {
@@ -33814,7 +34079,7 @@ function createAlterTypeAddValueOperationRuntime() {
33814
34079
  }
33815
34080
 
33816
34081
  // src/transition/operations/attach-logical-identity.ts
33817
- import { createHash as createHash5 } from "crypto";
34082
+ import { createHash as createHash6 } from "crypto";
33818
34083
  init_validate();
33819
34084
  function isRecord11(value) {
33820
34085
  return value != null && typeof value === "object" && !Array.isArray(value);
@@ -33957,7 +34222,7 @@ function renderInsertLogicalIdentitySql(_schema) {
33957
34222
  return `INSERT INTO ${qualifiedSideTable()} (logical_id, schema_name, table_name, column_name, carrier_kind) VALUES ($1, $2, $3, $4, $5)`;
33958
34223
  }
33959
34224
  function digest4(value) {
33960
- return createHash5("sha256").update(stableJson(value)).digest("hex");
34225
+ return createHash6("sha256").update(stableJson(value)).digest("hex");
33961
34226
  }
33962
34227
  function fact4(key, value) {
33963
34228
  return {
@@ -34360,7 +34625,7 @@ function createAttachLogicalIdentityOperationRuntime() {
34360
34625
  }
34361
34626
 
34362
34627
  // src/transition/operations/create-unique-index-concurrently.ts
34363
- import { createHash as createHash6 } from "crypto";
34628
+ import { createHash as createHash7 } from "crypto";
34364
34629
  init_validate();
34365
34630
  var DEFAULT_LOCK_TIMEOUT_MS = 5e3;
34366
34631
  function isRecord12(value) {
@@ -34472,7 +34737,7 @@ function operationPackSemanticsAssumption5(payload, context) {
34472
34737
  };
34473
34738
  }
34474
34739
  function digest5(value) {
34475
- return createHash6("sha256").update(stableJson(value)).digest("hex");
34740
+ return createHash7("sha256").update(stableJson(value)).digest("hex");
34476
34741
  }
34477
34742
  function fact5(key, value) {
34478
34743
  return {
@@ -35151,7 +35416,7 @@ function createCreateUniqueIndexConcurrentlyOperationRuntime() {
35151
35416
  }
35152
35417
 
35153
35418
  // src/transition/operations/manual-sql.ts
35154
- import { createHash as createHash7 } from "crypto";
35419
+ import { createHash as createHash8 } from "crypto";
35155
35420
  import { resourceScopeCovers } from "@dbsp/core";
35156
35421
  function isRecord13(value) {
35157
35422
  return value != null && typeof value === "object" && !Array.isArray(value);
@@ -35168,7 +35433,7 @@ function clientQuery6(client) {
35168
35433
  return queryable7(client.opaqueClient);
35169
35434
  }
35170
35435
  function digest6(value) {
35171
- return createHash7("sha256").update(stableJson(value)).digest("hex");
35436
+ return createHash8("sha256").update(stableJson(value)).digest("hex");
35172
35437
  }
35173
35438
  function fact6(key, value) {
35174
35439
  return {