MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / createD1ExecutorDb

Function createD1ExecutorDb

apps/host-cloudflare/src/db/d1.ts:87–154  ·  view source on GitHub ↗
(
  db: D1Database,
  blobs: R2Bucket | undefined,
)

Source from the content-addressed store, hash-verified

85// ---------------------------------------------------------------------------
86
87export const createD1ExecutorDb = async (
88 db: D1Database,
89 blobs: R2Bucket | undefined,
90): Promise<ExecutorDbHandle> => {
91 const options = {
92 tables: collectTables(),
93 namespace: CLOUDFLARE_NAMESPACE,
94 version: CLOUDFLARE_SCHEMA_VERSION,
95 provider: "sqlite" as const,
96 };
97
98 const schema = createDrizzleRuntimeSchemaFromTables(options);
99 const drizzleDb = drizzle(db, { schema });
100
101 // D1 rejects SQL `BEGIN TRANSACTION` / `SAVEPOINT` (it requires the JS batch
102 // API), and the shared ensure wraps its DDL in a transaction when the handle
103 // exposes one. A generated fingerprint lets every Worker/DO isolate prove the
104 // expected generated schema was already prepared with one primary read instead of
105 // replaying dozens of idempotent CREATE/ALTER statements on every database
106 // open. The marker is stored only after the ensure, data migrations, and any
107 // required post-compatibility ensure succeed. Data migrations retain their
108 // own ledger and still run below on every open.
109 const expectedFingerprint = await schemaFingerprint(
110 createDrizzleRuntimeSchemaSqlFromTables(options),
111 );
112 const preparedFingerprint = await readPreparedSchemaFingerprint(db);
113 const requiresSchemaPreparation = preparedFingerprint !== expectedFingerprint;
114 if (requiresSchemaPreparation) {
115 await ensureDrizzleRuntimeSchemaFromTables({ run: (query) => drizzleDb.run(query) }, options);
116 }
117 const migrationResult = await prepareCloudflareD1Data(db, blobs);
118 if (requiresSchemaPreparation) {
119 // Compatibility migrations can rebuild a legacy table. Re-run the
120 // idempotent ensure before stamping so the database matches the generated
121 // schema after migration, including newer nullable columns the legacy
122 // rebuild did not know about.
123 if (migrationResult.schemaChanged) {
124 await ensureDrizzleRuntimeSchemaFromTables({ run: (query) => drizzleDb.run(query) }, options);
125 }
126 await storePreparedSchemaFingerprint(db, expectedFingerprint);
127 }
128
129 // `interactiveTransactions: false` — D1 rejects interactive transactions, so
130 // the fuma adapter runs transaction callbacks directly (auto-commit per
131 // statement). Without this, every runtime write that wraps in a transaction
132 // (adding a source, etc.) emits `BEGIN` and 500s. libSQL keeps real
133 // transactions; D1 (same `provider: "sqlite"`) opts out here.
134 const { db: fumaDb, fuma } = createExecutorFumaDb(drizzleDb, {
135 ...options,
136 interactiveTransactions: false,
137 // D1 caps bound parameters at 100 per query; createMany batches to fit
138 // (otherwise a wide table like `tool` overflows with "too many SQL
139 // variables" when a source derives many tools).
140 maxBoundParameters: 100,
141 });
142
143 return {
144 db: fumaDb,

Callers 3

makeCloudflareAppFunction · 0.90
openSessionDbMethod · 0.90
d1.test.tsFile · 0.90

Tested by

no test coverage detected