@dolthub/doltlite 0.10.6 → 0.10.8

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/binding.gyp CHANGED
@@ -6,9 +6,6 @@
6
6
  "amalgamation/doltlite_orig.c",
7
7
  "amalgamation/doltlite.c",
8
8
  "amalgamation/blake3_sse2_impl.c",
9
- "amalgamation/blake3_sse41_impl.c",
10
- "amalgamation/blake3_avx2_impl.c",
11
- "amalgamation/blake3_avx512_impl.c",
12
9
  "amalgamation/blake3_neon_impl.c",
13
10
  "src/addon.cpp",
14
11
  "src/bun_compat.cpp",
@@ -26,7 +23,10 @@
26
23
  "SQLITE_ENABLE_JSON1",
27
24
  "SQLITE_ENABLE_RTREE",
28
25
  "SQLITE_ENABLE_COLUMN_METADATA",
29
- "DOLTLITE_PROLLY=1"
26
+ "DOLTLITE_PROLLY=1",
27
+ "BLAKE3_NO_SSE41=1",
28
+ "BLAKE3_NO_AVX2=1",
29
+ "BLAKE3_NO_AVX512=1"
30
30
  ],
31
31
  "cflags": ["-std=c11", "-fvisibility=hidden"],
32
32
  "cflags_cc": ["-std=c++17", "-fvisibility=hidden"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dolthub/doltlite",
3
- "version": "0.10.6",
3
+ "version": "0.10.8",
4
4
  "description": "Node.js bindings for DoltLite — a SQLite fork with Git-style version control",
5
5
  "license": "Apache-2.0",
6
6
  "main": "index.js",
@@ -12,6 +12,7 @@
12
12
  "src/",
13
13
  "binding.gyp",
14
14
  "scripts/download.js",
15
+ "scripts/build-amalgamation.js",
15
16
  "prebuilds/"
16
17
  ],
17
18
  "scripts": {
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,523 @@
1
+ #!/usr/bin/env node
2
+ // Builds a complete doltlite amalgamation from the autoconf source tree.
3
+ //
4
+ // Generates two C files:
5
+ //
6
+ // amalgamation/doltlite_orig.c — the original SQLite btree/pager/wal/backup code
7
+ // compiled with btree_orig_prefix.h so every exported symbol gets an orig_
8
+ // prefix. This keeps the original implementation available alongside the
9
+ // prolly-tree replacements. Compiled as a separate translation unit so its
10
+ // struct definitions (original BtShared, Pager, etc.) don't clash with the
11
+ // prolly versions in doltlite.c.
12
+ //
13
+ // amalgamation/doltlite.c — the base sqlite3.c amalgamation (with btree/pager/
14
+ // wal/btmutex/backup sections wrapped in #ifndef DOLTLITE_PROLLY), followed
15
+ // by the blake3, prolly-tree, and doltlite SQL-function source files.
16
+ //
17
+ // When both files are compiled with -DDOLTLITE_PROLLY=1, the full version-
18
+ // control stack is active. Without that flag doltlite.c degrades to plain SQLite
19
+ // and doltlite_orig.c is a harmless dead-code object.
20
+
21
+ "use strict"
22
+
23
+ const fs = require("fs")
24
+ const path = require("path")
25
+
26
+ const { version } = require("../package.json")
27
+
28
+ const srcRoot = path.join(__dirname, "../doltlite-src")
29
+ const baseSqlite = path.join(srcRoot, "sqlite3.c")
30
+ const srcDir = path.join(srcRoot, "src")
31
+ const blake3Dir = path.join(srcRoot, "ext", "blake3")
32
+ const outDir = path.join(__dirname, "../amalgamation")
33
+ const outFile = path.join(outDir, "doltlite.c")
34
+ const outOrig = path.join(outDir, "doltlite_orig.c")
35
+ const outHeader = path.join(outDir, "doltlite.h")
36
+
37
+ // Files in the sqlite3.c amalgamation whose symbols are replaced by prolly_btree.c / pager_shim.c.
38
+ // They are wrapped in #ifndef DOLTLITE_PROLLY so the compiler only sees one definition.
39
+ const GUARDED = ["btree.c", "pager.c", "wal.c", "btmutex.c", "backup.c"]
40
+
41
+ // Source files to append to doltlite.c.
42
+ // Each entry is either a filename relative to srcDir, or an absolute path.
43
+ // Order matters: lower-level files before the code that calls them.
44
+ const EXTRA = [
45
+ // blake3 hash library (required by prolly_hash.c, lives in ext/blake3/)
46
+ // blake3_dispatch.c replaced blake3_dispatch_portable.c in 0.10.6.
47
+ path.join(blake3Dir, "blake3.c"),
48
+ path.join(blake3Dir, "blake3_portable.c"),
49
+ path.join(blake3Dir, fs.existsSync(path.join(blake3Dir, "blake3_dispatch.c"))
50
+ ? "blake3_dispatch.c" : "blake3_dispatch_portable.c"),
51
+ // prolly tree
52
+ "prolly_hash.c",
53
+ "prolly_xxhash.c",
54
+ "prolly_hashset.c",
55
+ "prolly_node.c",
56
+ "prolly_cache.c",
57
+ // chunk store subsystems (split out of chunk_store.c in 0.10.8)
58
+ "chunk_file.c",
59
+ "chunk_refs.c",
60
+ "chunk_index.c",
61
+ "chunk_staging.c",
62
+ "chunk_wal.c",
63
+ "chunk_store.c",
64
+ "prolly_cursor.c",
65
+ "prolly_mutmap.c",
66
+ "prolly_chunker.c",
67
+ "prolly_mutate.c",
68
+ "prolly_diff.c",
69
+ "prolly_three_way_diff.c",
70
+ "prolly_three_way_merge.c",
71
+ "prolly_btree.c",
72
+ "pager_shim.c",
73
+ "sortkey.c",
74
+ // dolt SQL functions
75
+ "doltlite.c",
76
+ "doltlite_commit.c",
77
+ "doltlite_ref.c",
78
+ "doltlite_log.c",
79
+ "doltlite_status.c",
80
+ "doltlite_diff.c",
81
+ "doltlite_diff_table.c",
82
+ "doltlite_branch.c",
83
+ "doltlite_tag.c",
84
+ "doltlite_ancestor.c",
85
+ "doltlite_commit_ancestors.c",
86
+ "doltlite_merge.c",
87
+ "doltlite_schema_merge.c",
88
+ "doltlite_conflicts.c",
89
+ "doltlite_gc.c",
90
+ "doltlite_chunk_walk.c",
91
+ "doltlite_history.c",
92
+ "doltlite_at.c",
93
+ "doltlite_blame.c",
94
+ "doltlite_schema_diff.c",
95
+ "doltlite_schemas.c",
96
+ "doltlite_diff_stat.c",
97
+ "doltlite_record.c",
98
+ "doltlite_ignore.c",
99
+ "doltlite_hashof.c",
100
+ "doltlite_constraint_violations.c",
101
+ "doltlite_merge_constraints.c",
102
+ "doltlite_dbpage.c",
103
+ "doltlite_remote.c",
104
+ "doltlite_remote_sql.c",
105
+ "doltlite_http_remote.c",
106
+ "doltlite_remotesrv.c",
107
+ ]
108
+
109
+ // Per-file symbol renames to resolve conflicts when multiple source files are
110
+ // merged into a single translation unit.
111
+ // Each entry: { file: basename, renames: [[oldWord, newWord], ...] }
112
+ // Replacements are applied as whole-word substitutions (word-boundary regex).
113
+ const FILE_PATCHES = [
114
+ {
115
+ // prolly_btree.c defines struct TableEntry, struct SchemaEntry, and
116
+ // tableEntryNameCmp/buildSchemaCatalogRecord — rename the duplicates that
117
+ // appear later in doltlite_merge.c so they don't collide.
118
+ file: "doltlite_merge.c",
119
+ renames: [
120
+ // Rename the local MergeFieldValue (used only by buildSchemaCatalogRecord)
121
+ // so it doesn't clash with doltlite_merge_constraints.c's MergeFieldValue.
122
+ ["mergeCatalogSerialType", "dlMergeCatalogSerialType"],
123
+ ["mergeCatalogSerialPut", "dlMergeCatalogSerialPut"],
124
+ ["MergeFieldValue", "DlMergeSchemaMFV"],
125
+ // buildSchemaCatalogRecord is also defined in prolly_btree.c.
126
+ ["buildSchemaCatalogRecord","dlMergeBuildSchemaRecord"],
127
+ // ConflictRow is embedded in RowMergeCtx here; doltlite_conflicts.c also
128
+ // embeds an identically-named struct → rename the merge.c version.
129
+ ["ConflictRow", "DlMergeConflictRow"],
130
+ // freeConflictTables has a different signature in doltlite_conflicts.c.
131
+ ["freeConflictTables", "dlMergeFreeConflictTables"],
132
+ ],
133
+ },
134
+ {
135
+ // doltlite_merge_constraints.c defines its own MergeFieldValue (which has
136
+ // an extra `double r` field vs the one in doltlite_merge.c).
137
+ file: "doltlite_merge_constraints.c",
138
+ renames: [
139
+ ["mergeSerialType", "dlConstraintMergeSerialType"],
140
+ ["mergeSerialPut", "dlConstraintMergeSerialPut"],
141
+ ["MergeFieldValue", "DlConstraintMFV"],
142
+ ],
143
+ },
144
+ {
145
+ // writeAll is also defined (identically) in doltlite_http_remote.c which
146
+ // comes earlier in the EXTRA list.
147
+ file: "doltlite_remotesrv.c",
148
+ renames: [
149
+ ["writeAll", "srvWriteAll"],
150
+ ],
151
+ },
152
+ ]
153
+
154
+ // Per-file exact text patches (verbatim old → new, applied after renames).
155
+ // Each entry: { file: basename, patches: [[oldText, newText], ...] }
156
+ const FILE_TEXT_PATCHES = [
157
+ {
158
+ // os_win.c does not implement SQLITE_FCNTL_HAS_MOVED; sqlite3OsFileControl
159
+ // returns SQLITE_NOTFOUND ("unknown operation") for that opcode.
160
+ // csDetectExternalChanges previously propagated that error, so every write
161
+ // transaction on a file-based database failed on Windows. Treat NOTFOUND
162
+ // the same way the standard pager does: assume the file has not moved.
163
+ file: "chunk_store.c",
164
+ patches: [
165
+ // 0.10.5 used 2-space indent; 0.10.6 uses 4-space inside the hasMovedChecked guard.
166
+ [
167
+ ` rc = sqlite3OsFileControl(cs->file.pFile, SQLITE_FCNTL_HAS_MOVED, &bMoved);\n if( rc!=SQLITE_OK ) return rc;\n if( bMoved ){`,
168
+ ` rc = sqlite3OsFileControl(cs->file.pFile, SQLITE_FCNTL_HAS_MOVED, &bMoved);\n if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; /* not supported by this VFS */\n if( rc!=SQLITE_OK ) return rc;\n if( bMoved ){`,
169
+ ],
170
+ // On Windows, csFileLock / csFileLockNB opened the database file itself
171
+ // and held an exclusive byte-range lock over ALL bytes. sqlite3OsWrite
172
+ // then tried to write via cs->pFile (a different HANDLE to the same path).
173
+ // Windows exclusive byte-range locks block WriteFile from other handles —
174
+ // even handles in the same process — causing SQLITE_IOERR_WRITE after the
175
+ // winRetryIoerr retry loop. Fix: use a separate "<path>.lock" file so
176
+ // the lock fd and the I/O fd never conflict.
177
+ [
178
+ `#ifdef _WIN32\n# include <io.h>\n# include <windows.h>\n static int csFileLock(const char *path, int *pFd){\n int fd = _open(path, _O_BINARY | _O_RDWR | _O_CREAT, 0644);\n if( fd < 0 ) return -1;\n {\n HANDLE h = (HANDLE)_get_osfhandle(fd);\n OVERLAPPED ov = {0};\n if( !LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &ov) ){\n _close(fd);\n return -1;\n }\n }\n *pFd = fd;\n return 0;\n }\n static void csFileUnlock(int fd){\n if( fd >= 0 ){\n HANDLE h = (HANDLE)_get_osfhandle(fd);\n OVERLAPPED ov = {0};\n UnlockFileEx(h, 0, MAXDWORD, MAXDWORD, &ov);\n _close(fd);\n }\n }\n static int csFileLockNB(const char *path, int *pFd){\n int fd = _open(path, _O_BINARY | _O_RDWR | _O_CREAT, 0644);\n if( fd < 0 ) return -1;\n {\n HANDLE h = (HANDLE)_get_osfhandle(fd);\n OVERLAPPED ov = {0};\n if( !LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,\n 0, MAXDWORD, MAXDWORD, &ov) ){\n _close(fd);\n return -1;\n }\n }\n *pFd = fd;\n return 0;\n }\n#else`,
179
+ `#ifdef _WIN32\n# include <io.h>\n# include <windows.h>\n /* Use a separate "<path>.lock" file for byte-range locking so that\n ** LockFileEx on the lock fd does not conflict with WriteFile on\n ** cs->pFile (a different HANDLE to the same path). On Windows,\n ** exclusive byte-range locks block writes from other handles to the\n ** locked region even within the same process. */\n static int csMakeLockPath(const char *path, char **ppLock){\n int n = (int)strlen(path);\n char *p = (char *)malloc(n + 6);\n if( !p ) return -1;\n memcpy(p, path, n);\n memcpy(p + n, ".lock", 6);\n *ppLock = p;\n return 0;\n }\n static int csFileLock(const char *path, int *pFd){\n char *lockPath = 0;\n int fd;\n if( csMakeLockPath(path, &lockPath) ) return -1;\n fd = _open(lockPath, _O_BINARY | _O_RDWR | _O_CREAT, 0644);\n free(lockPath);\n if( fd < 0 ) return -1;\n {\n HANDLE h = (HANDLE)_get_osfhandle(fd);\n OVERLAPPED ov = {0};\n if( !LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &ov) ){\n _close(fd);\n return -1;\n }\n }\n *pFd = fd;\n return 0;\n }\n static void csFileUnlock(int fd){\n if( fd >= 0 ){\n HANDLE h = (HANDLE)_get_osfhandle(fd);\n OVERLAPPED ov = {0};\n UnlockFileEx(h, 0, MAXDWORD, MAXDWORD, &ov);\n _close(fd);\n }\n }\n static int csFileLockNB(const char *path, int *pFd){\n char *lockPath = 0;\n int fd;\n if( csMakeLockPath(path, &lockPath) ) return -1;\n fd = _open(lockPath, _O_BINARY | _O_RDWR | _O_CREAT, 0644);\n free(lockPath);\n if( fd < 0 ) return -1;\n {\n HANDLE h = (HANDLE)_get_osfhandle(fd);\n OVERLAPPED ov = {0};\n if( !LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,\n 0, MAXDWORD, MAXDWORD, &ov) ){\n _close(fd);\n return -1;\n }\n }\n *pFd = fd;\n return 0;\n }\n#else`,
180
+ ],
181
+ ],
182
+ },
183
+ ]
184
+
185
+ // Build a map from basename → rename pairs for fast lookup.
186
+ const patchMap = new Map()
187
+ for (const { file, renames } of FILE_PATCHES) {
188
+ patchMap.set(file, renames)
189
+ }
190
+
191
+ // Build a map from basename → text patches for fast lookup.
192
+ const textPatchMap = new Map()
193
+ for (const { file, patches } of FILE_TEXT_PATCHES) {
194
+ textPatchMap.set(file, patches)
195
+ }
196
+
197
+ function applyRenames(src, renames) {
198
+ let out = src
199
+ for (const [from, to] of renames) {
200
+ // Word-boundary replacement: only replace complete identifiers.
201
+ const re = new RegExp(`\\b${from}\\b`, "g")
202
+ out = out.replace(re, to)
203
+ }
204
+ return out
205
+ }
206
+
207
+ function applyTextPatches(src, patches) {
208
+ let out = src
209
+ for (const [from, to] of patches) {
210
+ if (!out.includes(from)) {
211
+ process.stderr.write(`build-amalgamation: warning — text patch not found in source\n expected: ${JSON.stringify(from.slice(0, 60))}\n`)
212
+ }
213
+ out = out.split(from).join(to)
214
+ }
215
+ return out
216
+ }
217
+
218
+ if (!fs.existsSync(baseSqlite)) {
219
+ console.error(`build-amalgamation: ${baseSqlite} not found — run download.js first`)
220
+ process.exit(1)
221
+ }
222
+
223
+ console.log("Building full doltlite amalgamation...")
224
+
225
+ // Extract generated headers (parse.h, opcodes.h) from the sqlite3.c amalgamation.
226
+ // These files are produced by the SQLite build system and are not shipped in the tarball.
227
+ {
228
+ const base = fs.readFileSync(baseSqlite, "utf8")
229
+ for (const hdr of ["parse.h", "opcodes.h"]) {
230
+ const hdrPath = path.join(srcDir, hdr)
231
+ if (fs.existsSync(hdrPath)) continue
232
+ const beginMark = `/************** Begin file ${hdr}`
233
+ const endMark = `/************** End of ${hdr}`
234
+ const bi = base.indexOf(beginMark)
235
+ const ei = base.indexOf(endMark)
236
+ if (bi !== -1 && ei !== -1) {
237
+ const startOfContent = base.indexOf("\n", bi) + 1
238
+ const endOfContent = base.lastIndexOf("\n", ei) + 1
239
+ fs.writeFileSync(hdrPath, base.slice(startOfContent, endOfContent))
240
+ console.log(`Extracted ${hdr} to ${hdrPath}`)
241
+ } else {
242
+ process.stderr.write(`build-amalgamation: warning — could not find ${hdr} in sqlite3.c\n`)
243
+ }
244
+ }
245
+ }
246
+
247
+ // Patch btreeInt.h to add include guards.
248
+ // The original file has none; when multiple _orig.c files (each of which does
249
+ // #include "btmutex.c" / "backup.c" / etc.) are merged into a single translation
250
+ // unit, the compiler re-includes btreeInt.h through each of them, triggering
251
+ // struct redefinition errors.
252
+ {
253
+ const btreeIntPath = path.join(srcDir, "btreeInt.h")
254
+ if (fs.existsSync(btreeIntPath)) {
255
+ const src = fs.readFileSync(btreeIntPath, "utf8")
256
+ if (!src.includes("#ifndef BTREEINT_H")) {
257
+ fs.writeFileSync(btreeIntPath,
258
+ "#ifndef BTREEINT_H\n#define BTREEINT_H\n" + src + "\n#endif /* BTREEINT_H */\n"
259
+ )
260
+ console.log("Patched btreeInt.h with include guards")
261
+ }
262
+ }
263
+ }
264
+
265
+ // ── doltlite_orig.c ────────────────────────────────────────────────────────
266
+ // Compile the original SQLite btree/pager/wal/backup with orig_ prefix symbols.
267
+ // This must be a separate TU because it defines the original BtShared / Pager
268
+ // structs, which conflict with prolly_btree.c's replacements in doltlite.c.
269
+ {
270
+ const origFiles = [
271
+ "btmutex_orig.c",
272
+ "pager_orig.c",
273
+ "wal_orig.c",
274
+ "backup_orig.c",
275
+ "btree_orig.c",
276
+ "btree_orig_api.c",
277
+ ]
278
+
279
+ // Variables defined in the original pager/btree code that pager_shim.c in
280
+ // doltlite.c also defines. We rename them here so the linker sees two
281
+ // different symbols and doesn't complain about duplicate definitions.
282
+ // (These are test/statistics counters, only incremented under #ifdef SQLITE_TEST,
283
+ // so renaming them away from their original names has no runtime effect in
284
+ // release builds.)
285
+ const conflictingGlobals = [
286
+ "sqlite3_pager_readdb_count",
287
+ "sqlite3_pager_writedb_count",
288
+ "sqlite3_pager_writej_count",
289
+ "sqlite3_opentemp_count",
290
+ "sqlite3SharedCacheList",
291
+ ]
292
+
293
+ const origLines = [
294
+ "/* Generated by build-amalgamation.js — DO NOT EDIT */",
295
+ "/* Original SQLite btree/pager/wal/backup compiled with orig_ prefix symbols.",
296
+ "** Compiled as a separate translation unit from doltlite.c. */",
297
+ "",
298
+ "/* btree_orig_api.c needs DOLTLITE_PROLLY */",
299
+ "#ifndef DOLTLITE_PROLLY",
300
+ "#define DOLTLITE_PROLLY 1",
301
+ "#endif",
302
+ "",
303
+ "/* Rename globals that pager_shim.c in doltlite.c also defines, to avoid",
304
+ "** duplicate-symbol linker errors in release (non-SQLITE_TEST) builds. */",
305
+ ...conflictingGlobals.map(g => `#define ${g} dlt_orig_${g.replace(/^sqlite3_?/, "")}`),
306
+ "",
307
+ ]
308
+
309
+ for (const f of origFiles) {
310
+ const p = path.join(srcDir, f)
311
+ if (!fs.existsSync(p)) {
312
+ process.stderr.write(`build-amalgamation: warning — ${f} not found, skipping\n`)
313
+ continue
314
+ }
315
+ origLines.push(`/************** Begin doltlite_orig file ${f} **************/`)
316
+ origLines.push(fs.readFileSync(p, "utf8"))
317
+ origLines.push(`/************** End of doltlite_orig file ${f} ******************/`)
318
+ origLines.push("")
319
+ }
320
+
321
+ fs.mkdirSync(outDir, { recursive: true })
322
+ fs.writeFileSync(outOrig, origLines.join("\n"))
323
+ console.log(`doltlite_orig.c written to ${outOrig}`)
324
+ }
325
+
326
+ // ── doltlite.c ────────────────────────────────────────────────────────────
327
+ let content = fs.readFileSync(baseSqlite, "utf8")
328
+
329
+ // Make SQLITE_PRIVATE symbols non-static so doltlite_orig.c (a separate TU
330
+ // that contains the original pager/btree/wal code) can link against them.
331
+ // The amalgamation defines SQLITE_PRIVATE as `static`, which gives every
332
+ // internal function internal linkage — invisible to other TUs. Removing
333
+ // the `static` makes them hidden-visibility globals (via -fvisibility=hidden)
334
+ // that are linkable within the final .node binary but not exported from it.
335
+ content = content.replace(/#\s*define\s+SQLITE_PRIVATE\s+static\b/, '# define SQLITE_PRIVATE')
336
+
337
+ // Wrap each guarded section.
338
+ // The amalgamation uses markers of the form:
339
+ // /************** Begin file btree.c **************/
340
+ // /************** End of btree.c ******************/
341
+ for (const file of GUARDED) {
342
+ const escapedFile = file.replace(".", "\\.")
343
+ const beginRe = new RegExp(`(/\\*{6,}\\s*Begin file ${escapedFile}\\s*\\*{6,}/)`)
344
+ const endRe = new RegExp(`(/\\*{6,}\\s*End of ${escapedFile}\\s*\\*{6,}/)`)
345
+ const bm = beginRe.exec(content)
346
+ const em = endRe.exec(content)
347
+ if (!bm || !em) {
348
+ process.stderr.write(`build-amalgamation: warning — markers not found for ${file}\n`)
349
+ continue
350
+ }
351
+ const before = content.slice(0, bm.index)
352
+ const section = content.slice(bm.index, em.index + em[0].length)
353
+ const after = content.slice(em.index + em[0].length)
354
+ content = `${before}#ifndef DOLTLITE_PROLLY\n${section}\n#endif /* !DOLTLITE_PROLLY */\n${after}`
355
+ }
356
+
357
+ // Append prolly + doltlite source files.
358
+ const parts = [content]
359
+ for (const file of EXTRA) {
360
+ const p = path.isAbsolute(file) ? file : path.join(srcDir, file)
361
+ const name = path.basename(p)
362
+ if (!fs.existsSync(p)) {
363
+ process.stderr.write(`build-amalgamation: warning — ${name} not found, skipping\n`)
364
+ continue
365
+ }
366
+ let src = fs.readFileSync(p, "utf8")
367
+ if (patchMap.has(name)) {
368
+ src = applyRenames(src, patchMap.get(name))
369
+ }
370
+ if (textPatchMap.has(name)) {
371
+ src = applyTextPatches(src, textPatchMap.get(name))
372
+ }
373
+ parts.push(`\n/************** Begin DOLTLITE-EXTRA file ${name} **************/\n`)
374
+ parts.push(src)
375
+ parts.push(`\n/************** End of DOLTLITE-EXTRA ${name} ******************/\n`)
376
+ }
377
+
378
+ // Prepend the DOLTLITE_VERSION define into the extra section so that
379
+ // prolly_btree.c and doltlite.c can use it without compiler flag quoting issues.
380
+ parts.splice(1, 0, `\n/* Injected by build-amalgamation.js */\n#ifndef DOLTLITE_VERSION\n#define DOLTLITE_VERSION "${version}"\n#endif\n`)
381
+
382
+ // sqlite3PagerWalSystemErrno is defined in pager.c, which is skipped in
383
+ // DOLTLITE_PROLLY builds. pager_shim.c does not implement it. Provide a
384
+ // stub: shim-pagers have no WAL (return 0); plain-sqlite Pagers (from ATTACH)
385
+ // dispatch to the orig_ version compiled in doltlite_orig.c.
386
+ // strncasecmp is a POSIX extension absent from MSVC — map it to _strnicmp.
387
+ parts.push(`
388
+ #ifdef DOLTLITE_PROLLY
389
+ #ifdef _WIN32
390
+ # ifndef strncasecmp
391
+ # define strncasecmp _strnicmp
392
+ # endif
393
+ #endif
394
+ extern int orig_sqlite3PagerWalSystemErrno(Pager *pPager);
395
+ int sqlite3PagerWalSystemErrno(Pager *pPager){
396
+ if( !pPager ) return 0;
397
+ /* Shim pagers start with PAGER_SHIM_MAGIC (0x50534D31) at offset 0. */
398
+ if( ((const unsigned int*)pPager)[0] == 0x50534D31u ) return 0;
399
+ return orig_sqlite3PagerWalSystemErrno(pPager);
400
+ }
401
+ #endif /* DOLTLITE_PROLLY */
402
+ `)
403
+
404
+ fs.mkdirSync(outDir, { recursive: true })
405
+ fs.writeFileSync(outFile, parts.join(""))
406
+ console.log(`Amalgamation written to ${outFile}`)
407
+
408
+ // Generate a shim doltlite_internal.h in the output directory.
409
+ // Since amalgamation/ is listed first in include_dirs, this takes priority
410
+ // over the one in doltlite-src/src/. It wraps the definitions that
411
+ // prolly_btree.c already provides so they don't get redefined.
412
+ const origInternal = fs.readFileSync(
413
+ path.join(srcDir, "doltlite_internal.h"), "utf8"
414
+ )
415
+ // prolly_btree.c (compiled earlier in the same TU) already defines:
416
+ // struct TableEntry, typedef/struct SchemaEntry, tableEntryNameCmp
417
+ // Wrap those in #ifndef DOLTLITE_PROLLY so they're skipped in prolly mode.
418
+ let shimInternal = origInternal
419
+
420
+ // Guard `struct TableEntry { ... };`
421
+ shimInternal = shimInternal.replace(
422
+ /(struct TableEntry \{[^}]*\};)/s,
423
+ "#ifndef DOLTLITE_PROLLY\n$1\n#endif /* !DOLTLITE_PROLLY */"
424
+ )
425
+
426
+ // Guard `typedef struct SchemaEntry SchemaEntry;`
427
+ shimInternal = shimInternal.replace(
428
+ /typedef struct SchemaEntry SchemaEntry;/,
429
+ "#ifndef DOLTLITE_PROLLY\ntypedef struct SchemaEntry SchemaEntry;\n#endif /* !DOLTLITE_PROLLY */"
430
+ )
431
+
432
+ // Guard `struct SchemaEntry { ... };` (at end of file)
433
+ shimInternal = shimInternal.replace(
434
+ /(struct SchemaEntry \{[^}]*\};)/s,
435
+ "#ifndef DOLTLITE_PROLLY\n$1\n#endif /* !DOLTLITE_PROLLY */"
436
+ )
437
+
438
+ // Guard `tableEntryNameCmp` static inline function
439
+ shimInternal = shimInternal.replace(
440
+ /(static SQLITE_INLINE int tableEntryNameCmp\([\s\S]*?\n\})/,
441
+ "#ifndef DOLTLITE_PROLLY\n$1\n#endif /* !DOLTLITE_PROLLY */"
442
+ )
443
+
444
+ const shimPath = path.join(outDir, "doltlite_internal.h")
445
+ fs.writeFileSync(shimPath, shimInternal)
446
+
447
+ // Copy the header alongside.
448
+ const baseHeader = path.join(srcRoot, "sqlite3.h")
449
+ if (fs.existsSync(baseHeader) && !fs.existsSync(outHeader)) {
450
+ fs.copyFileSync(baseHeader, outHeader)
451
+ }
452
+
453
+ console.log(`Shim doltlite_internal.h written to ${shimPath}`)
454
+
455
+ // Copy all .h files from doltlite-src/src/ and doltlite-src/ext/blake3/ into
456
+ // amalgamation/ so the compiler can find them via the existing "amalgamation"
457
+ // include_dir without needing doltlite-src in the C include path.
458
+ // Skip files that already have custom shims (btreeInt.h, doltlite_internal.h).
459
+ const SKIP_HEADERS = new Set(["btreeInt.h", "doltlite_internal.h"])
460
+ for (const hdrDir of [srcRoot, srcDir, blake3Dir]) {
461
+ if (!fs.existsSync(hdrDir)) continue
462
+ for (const f of fs.readdirSync(hdrDir)) {
463
+ if (!f.endsWith(".h") || SKIP_HEADERS.has(f)) continue
464
+ const dest = path.join(outDir, f)
465
+ if (!fs.existsSync(dest)) {
466
+ fs.copyFileSync(path.join(hdrDir, f), dest)
467
+ }
468
+ }
469
+ }
470
+
471
+ // The _orig.c wrapper files each textually #include their base SQLite .c file
472
+ // (e.g. btmutex_orig.c does #include "btmutex.c"). When those wrappers are
473
+ // inlined into doltlite_orig.c the compiler resolves the include relative to
474
+ // amalgamation/, so the base .c files must be present there too.
475
+ for (const f of ["btmutex.c", "pager.c", "wal.c", "backup.c", "btree.c"]) {
476
+ const src = path.join(srcDir, f)
477
+ const dest = path.join(outDir, f)
478
+ if (fs.existsSync(src) && !fs.existsSync(dest)) {
479
+ fs.copyFileSync(src, dest)
480
+ }
481
+ }
482
+
483
+ // Generate amalgamation/btreeInt.h — adds include guards that the original lacks.
484
+ // Without guards, each _orig.c file in doltlite_orig.c re-includes btreeInt.h via
485
+ // sqliteInt.h, causing struct redefinition errors in a single translation unit.
486
+ // The shim must NOT have its own #ifndef BTREEINT_H guard: we already patch the
487
+ // original btreeInt.h to add that guard, so if the shim defined it first the
488
+ // original's body would be skipped and struct Btree would never be declared.
489
+ const btreeIntShim = [
490
+ "/* Generated by build-amalgamation.js — DO NOT EDIT */",
491
+ "/* Redirects to doltlite-src/src/btreeInt.h (which has its own include guard). */",
492
+ `#include "../doltlite-src/src/btreeInt.h"`,
493
+ "",
494
+ ].join("\n")
495
+ fs.writeFileSync(path.join(outDir, "btreeInt.h"), btreeIntShim)
496
+ console.log(`Shim btreeInt.h written to ${path.join(outDir, "btreeInt.h")}`)
497
+
498
+ // Generate SIMD wrapper files for BLAKE3.
499
+ //
500
+ // SSE4.1, AVX2, and AVX-512 are disabled via BLAKE3_NO_* defines in binding.gyp
501
+ // because they require per-file compiler flags that GYP can't express portably.
502
+ // SSE2 (baseline on x86_64) and NEON (baseline on aarch64) need no special flags
503
+ // and are compiled here as separate translation units.
504
+ const simdWrappers = [
505
+ // name in amalgamation/ source file arch guard
506
+ ["blake3_sse2_impl.c", "blake3_sse2.c", "defined(__x86_64__)||defined(_M_X64)||defined(__i386__)"],
507
+ ["blake3_neon_impl.c", "blake3_neon.c", "defined(__aarch64__)||defined(__arm__)"],
508
+ ]
509
+
510
+ for (const [wrapName, srcName, archGuard] of simdWrappers) {
511
+ const srcPath = path.join(blake3Dir, srcName)
512
+ if (!fs.existsSync(srcPath)) continue
513
+ const relSrc = path.relative(outDir, srcPath).replace(/\\/g, "/")
514
+ const lines = [
515
+ "/* Generated by build-amalgamation.js — DO NOT EDIT */",
516
+ `#if ${archGuard}`,
517
+ `#include "${relSrc}"`,
518
+ "#endif",
519
+ "",
520
+ ]
521
+ fs.writeFileSync(path.join(outDir, wrapName), lines.join("\n"))
522
+ console.log(`SIMD wrapper ${wrapName} written`)
523
+ }