@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 +4 -4
- package/package.json +2 -1
- package/prebuilds/darwin-arm64/doltlite +0 -0
- package/prebuilds/darwin-arm64/doltlite.node +0 -0
- package/prebuilds/darwin-x64/doltlite.node +0 -0
- package/prebuilds/linux-arm64/doltlite.node +0 -0
- package/prebuilds/linux-x64/doltlite +0 -0
- package/prebuilds/linux-x64/doltlite.node +0 -0
- package/prebuilds/win32-x64/doltlite.exe +0 -0
- package/prebuilds/win32-x64/doltlite.node +0 -0
- package/scripts/build-amalgamation.js +523 -0
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.
|
|
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
|
|
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
|
+
}
|