Compare commits

...

17 Commits

Author SHA1 Message Date
Jarred Sumner
8c74055352 Merge branch 'main' into claude/sqlite-dynamic-loading-linux 2025-09-30 14:59:26 -07:00
Jarred Sumner
1acaa618c4 Merge branch 'main' into claude/sqlite-dynamic-loading-linux 2025-09-26 19:23:53 -07:00
Claude Bot
8eea8c6d7c Fix SQLite dynamic loading logic on Linux
The condition in lazyLoadSQLite() was inverted, causing Linux builds
to incorrectly attempt dynamic loading of libsqlite3.so even when
using static linking (the default).

Changed the condition from:
  if (use_static_sqlite && _user_overriden_sqlite3_lib_path != nullptr)
to:
  if (use_static_sqlite && _user_overriden_sqlite3_lib_path == nullptr)

This ensures that on Linux with static SQLite (default), the function
returns early without attempting to dlopen libsqlite3.so, unless a
custom library path has been explicitly set.

Fixes 9 out of 10 requested tests. The remaining test failure in
import-attributes.test.ts is unrelated to SQLite.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-09 04:47:54 +00:00
Jarred Sumner
a90632f658 Update lazy_sqlite3.h 2025-09-06 00:54:53 -07:00
Jarred Sumner
f9ff380a8d Update lazy_sqlite3.h 2025-09-06 00:54:16 -07:00
Jarred Sumner
f7d7217cc9 Update lazy_sqlite3.h 2025-09-06 00:52:03 -07:00
Jarred Sumner
56d4480e5f Revert "Update static-initializers.test.ts"
This reverts commit 1977b029d8.
2025-09-06 00:51:14 -07:00
Jarred Sumner
e1febfad2d Update lazy_sqlite3.h 2025-09-06 00:51:09 -07:00
Jarred Sumner
1977b029d8 Update static-initializers.test.ts 2025-09-05 23:48:36 -07:00
Jarred Sumner
0f05bf49cc Update JSSQLStatement.cpp 2025-09-05 22:30:03 -07:00
Jarred Sumner
b6a4ad805c Update lazy_sqlite3.h 2025-09-05 22:09:10 -07:00
Jarred Sumner
d2df086596 lazier 2025-09-05 21:49:15 -07:00
Jarred Sumner
7f8ddacfac Merge branch 'main' into claude/sqlite-dynamic-loading-linux 2025-09-05 20:42:55 -07:00
autofix-ci[bot]
2d5379c63c [autofix.ci] apply automated fixes 2025-09-06 00:09:55 +00:00
Claude Bot
eb851aead3 refactor: Simplify SQLite dynamic loading with static initialization
Instead of initializing function pointers at runtime, we now use static
initialization at compile time:

- On macOS (LAZY_LOAD_SQLITE=1): Function pointers start as nullptr and are
  loaded dynamically from the dylib
- On Linux (LAZY_LOAD_SQLITE=0): Function pointers are statically initialized
  to the actual SQLite functions at compile time

This approach is simpler and more efficient:
- No runtime overhead for Linux static builds
- Cleaner code with less conditional logic in lazyLoadSQLite()
- Function pointers are always ready to use on Linux

The symbol aliasing works by having the function pointers directly point to
the statically linked SQLite symbols, avoiding any runtime initialization cost.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-06 00:07:19 +00:00
Claude Bot
7f670cd741 fix: Use std::string for sqlite3_lib_path to avoid dangling pointer
The previous implementation was storing a pointer to temporary data from
utf8().data() which would be deallocated, causing undefined behavior.
Now using std::string to properly manage the lifetime of the path string.
2025-09-05 23:47:58 +00:00
Claude Bot
74695aaf78 feat(sqlite): Add optional dynamic SQLite loading support on Linux
This adds support for optionally dynamically loading SQLite on Linux, similar to
the existing macOS support. By default, Linux continues to use the statically
linked SQLite, but users can now call Database.setCustomSQLite() before first
use to load a custom SQLite library.

Key changes:
- Updated LAZY_LOAD_SQLITE preprocessor logic to always include dynamic loading infrastructure
- Modified lazy_sqlite3.h to support both static (default on Linux) and dynamic loading
- Linux defaults to static linking unless setCustomSQLite is called
- Added tests for the new functionality

The implementation maintains backward compatibility - Linux builds continue using
static SQLite by default, with dynamic loading as an opt-in feature via the
setCustomSQLite API.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-05 21:32:07 +00:00
4 changed files with 262 additions and 96 deletions

View File

@@ -67,16 +67,9 @@ static constexpr int32_t kStrictFlag = 1 << 2;
// we only call one pointer for the actual library
// and it means there's less work for DYLD to do on startup
// i.e. it shouldn't have any impact on startup time
#if LAZY_LOAD_SQLITE
// Always include lazy loading support
#include "lazy_sqlite3.h"
#else
static inline int lazyLoadSQLite()
{
return 0;
}
#endif
/* ******************************************************************************** */
#if !USE(SYSTEM_MALLOC)
@@ -1121,14 +1114,15 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementSetCustomSQLite, (JSC::JSGlobalObject * l
return {};
}
#if LAZY_LOAD_SQLITE
if (sqlite3_handle) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "SQLite already loaded\nThis function can only be called before SQLite has been loaded and exactly once. SQLite auto-loads when the first time you open a Database."_s));
return {};
}
sqlite3_lib_path = sqliteStrValue.toWTFString(lexicalGlobalObject).utf8().data();
// Mark that we want to use dynamic loading
auto pathString = sqliteStrValue.toWTFString(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
setSQLiteLibPath(std::string(pathString.utf8().data()));
if (lazyLoadSQLite() == -1) {
sqlite3_handle = nullptr;
@@ -1136,7 +1130,6 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementSetCustomSQLite, (JSC::JSGlobalObject * l
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, msg));
return {};
}
#endif
initializeSQLite();
@@ -1184,13 +1177,11 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementDeserialize, (JSC::JSGlobalObject * lexic
return {};
}
#if LAZY_LOAD_SQLITE
if (lazyLoadSQLite() < 0) [[unlikely]] {
WTF::String msg = WTF::String::fromUTF8(dlerror());
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, msg));
return {};
}
#endif
initializeSQLite();
size_t byteLength = array->byteLength();
@@ -1645,13 +1636,11 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementOpenStatementFunction, (JSC::JSGlobalObje
return {};
}
#if LAZY_LOAD_SQLITE
if (lazyLoadSQLite() < 0) [[unlikely]] {
WTF::String msg = WTF::String::fromUTF8(dlerror());
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, msg));
return {};
}
#endif
initializeSQLite();
auto catchScope = DECLARE_CATCH_SCOPE(vm);

View File

@@ -35,27 +35,12 @@
#include "BunClientData.h"
#include <JavaScriptCore/CallFrame.h>
#ifndef LAZY_LOAD_SQLITE_DEFAULT_SETTING
#if defined(__APPLE__)
#define LAZY_LOAD_SQLITE_DEFAULT_SETTING 1
#endif
#endif
// CMake controls whether we use lazy loading
// On macOS: LAZY_LOAD_SQLITE=1 (dynamic by default)
// On Linux: LAZY_LOAD_SQLITE=0 (static by default, but can switch to dynamic)
#ifndef LAZY_LOAD_SQLITE
#ifdef LAZY_LOAD_SQLITE_DEFAULT_SETTING
#define LAZY_LOAD_SQLITE LAZY_LOAD_SQLITE_DEFAULT_SETTING
#endif
#endif
#ifndef LAZY_LOAD_SQLITE
#define LAZY_LOAD_SQLITE 0
#endif
#if LAZY_LOAD_SQLITE
#include "sqlite3.h"
#else
// Always use local sqlite3 header for definitions
#include "sqlite3_local.h"
#endif
namespace WebCore {

View File

@@ -1,7 +1,7 @@
#pragma once
#include "root.h"
#include "sqlite3.h"
#include "sqlite3_local.h"
#if !OS(WINDOWS)
#include <dlfcn.h>
@@ -38,7 +38,7 @@ typedef const char* (*lazy_sqlite3_column_name_type)(sqlite3_stmt*, int N);
typedef const char* (*lazy_sqlite3_errmsg_type)(sqlite3*);
typedef int (*lazy_sqlite3_extended_errcode_type)(sqlite3*);
typedef int (*lazy_sqlite3_error_offset_type)(sqlite3*);
typedef int64_t (*lazy_sqlite3_memory_used_type)();
typedef sqlite3_int64 (*lazy_sqlite3_memory_used_type)();
typedef const char* (*lazy_sqlite3_errstr_type)(int);
typedef char* (*lazy_sqlite3_expanded_sql_type)(sqlite3_stmt* pStmt);
typedef int (*lazy_sqlite3_finalize_type)(sqlite3_stmt* pStmt);
@@ -93,61 +93,121 @@ typedef int (*lazy_sqlite3_deserialize_type)(
typedef int (*lazy_sqlite3_stmt_readonly_type)(sqlite3_stmt* pStmt);
typedef int (*lazy_sqlite3_stmt_busy_type)(sqlite3_stmt* pStmt);
typedef int (*lazy_sqlite3_compileoption_used_type)(const char* zOptName);
typedef int64_t (*lazy_sqlite3_last_insert_rowid_type)(sqlite3* db);
typedef sqlite3_int64 (*lazy_sqlite3_last_insert_rowid_type)(sqlite3* db);
static lazy_sqlite3_bind_blob_type lazy_sqlite3_bind_blob;
static lazy_sqlite3_bind_double_type lazy_sqlite3_bind_double;
static lazy_sqlite3_bind_int_type lazy_sqlite3_bind_int;
static lazy_sqlite3_bind_int64_type lazy_sqlite3_bind_int64;
static lazy_sqlite3_bind_null_type lazy_sqlite3_bind_null;
static lazy_sqlite3_bind_parameter_count_type lazy_sqlite3_bind_parameter_count;
static lazy_sqlite3_bind_parameter_index_type lazy_sqlite3_bind_parameter_index;
static lazy_sqlite3_bind_text_type lazy_sqlite3_bind_text;
static lazy_sqlite3_bind_text16_type lazy_sqlite3_bind_text16;
static lazy_sqlite3_changes_type lazy_sqlite3_changes;
static lazy_sqlite3_clear_bindings_type lazy_sqlite3_clear_bindings;
static lazy_sqlite3_close_v2_type lazy_sqlite3_close_v2;
static lazy_sqlite3_close_type lazy_sqlite3_close;
static lazy_sqlite3_file_control_type lazy_sqlite3_file_control;
static lazy_sqlite3_column_blob_type lazy_sqlite3_column_blob;
static lazy_sqlite3_column_bytes_type lazy_sqlite3_column_bytes;
static lazy_sqlite3_column_bytes16_type lazy_sqlite3_column_bytes16;
static lazy_sqlite3_column_count_type lazy_sqlite3_column_count;
static lazy_sqlite3_column_decltype_type lazy_sqlite3_column_decltype;
static lazy_sqlite3_column_double_type lazy_sqlite3_column_double;
static lazy_sqlite3_column_int_type lazy_sqlite3_column_int;
static lazy_sqlite3_column_int64_type lazy_sqlite3_column_int64;
static lazy_sqlite3_column_name_type lazy_sqlite3_column_name;
static lazy_sqlite3_column_text_type lazy_sqlite3_column_text;
static lazy_sqlite3_column_type_type lazy_sqlite3_column_type;
static lazy_sqlite3_errmsg_type lazy_sqlite3_errmsg;
static lazy_sqlite3_errstr_type lazy_sqlite3_errstr;
static lazy_sqlite3_expanded_sql_type lazy_sqlite3_expanded_sql;
static lazy_sqlite3_finalize_type lazy_sqlite3_finalize;
static lazy_sqlite3_free_type lazy_sqlite3_free;
static lazy_sqlite3_get_autocommit_type lazy_sqlite3_get_autocommit;
static lazy_sqlite3_open_v2_type lazy_sqlite3_open_v2;
static lazy_sqlite3_prepare_v3_type lazy_sqlite3_prepare_v3;
static lazy_sqlite3_prepare16_v3_type lazy_sqlite3_prepare16_v3;
static lazy_sqlite3_reset_type lazy_sqlite3_reset;
static lazy_sqlite3_step_type lazy_sqlite3_step;
static lazy_sqlite3_db_config_type lazy_sqlite3_db_config;
static lazy_sqlite3_load_extension_type lazy_sqlite3_load_extension;
static lazy_sqlite3_malloc64_type lazy_sqlite3_malloc64;
static lazy_sqlite3_serialize_type lazy_sqlite3_serialize;
static lazy_sqlite3_deserialize_type lazy_sqlite3_deserialize;
static lazy_sqlite3_stmt_readonly_type lazy_sqlite3_stmt_readonly;
static lazy_sqlite3_stmt_busy_type lazy_sqlite3_stmt_busy;
static lazy_sqlite3_compileoption_used_type lazy_sqlite3_compileoption_used;
static lazy_sqlite3_config_type lazy_sqlite3_config;
static lazy_sqlite3_extended_result_codes_type lazy_sqlite3_extended_result_codes;
static lazy_sqlite3_extended_errcode_type lazy_sqlite3_extended_errcode;
static lazy_sqlite3_error_offset_type lazy_sqlite3_error_offset;
static lazy_sqlite3_memory_used_type lazy_sqlite3_memory_used;
static lazy_sqlite3_bind_parameter_name_type lazy_sqlite3_bind_parameter_name;
static lazy_sqlite3_total_changes_type lazy_sqlite3_total_changes;
static lazy_sqlite3_last_insert_rowid_type lazy_sqlite3_last_insert_rowid;
#if LAZY_LOAD_SQLITE
// On macOS: start with null pointers that will be dynamically loaded
static lazy_sqlite3_bind_blob_type lazy_sqlite3_bind_blob = nullptr;
static lazy_sqlite3_bind_double_type lazy_sqlite3_bind_double = nullptr;
static lazy_sqlite3_bind_int_type lazy_sqlite3_bind_int = nullptr;
static lazy_sqlite3_bind_int64_type lazy_sqlite3_bind_int64 = nullptr;
static lazy_sqlite3_bind_null_type lazy_sqlite3_bind_null = nullptr;
static lazy_sqlite3_bind_parameter_count_type lazy_sqlite3_bind_parameter_count = nullptr;
static lazy_sqlite3_bind_parameter_index_type lazy_sqlite3_bind_parameter_index = nullptr;
static lazy_sqlite3_bind_text_type lazy_sqlite3_bind_text = nullptr;
static lazy_sqlite3_bind_text16_type lazy_sqlite3_bind_text16 = nullptr;
static lazy_sqlite3_changes_type lazy_sqlite3_changes = nullptr;
static lazy_sqlite3_clear_bindings_type lazy_sqlite3_clear_bindings = nullptr;
static lazy_sqlite3_close_v2_type lazy_sqlite3_close_v2 = nullptr;
static lazy_sqlite3_close_type lazy_sqlite3_close = nullptr;
static lazy_sqlite3_file_control_type lazy_sqlite3_file_control = nullptr;
static lazy_sqlite3_column_blob_type lazy_sqlite3_column_blob = nullptr;
static lazy_sqlite3_column_bytes_type lazy_sqlite3_column_bytes = nullptr;
static lazy_sqlite3_column_bytes16_type lazy_sqlite3_column_bytes16 = nullptr;
static lazy_sqlite3_column_count_type lazy_sqlite3_column_count = nullptr;
static lazy_sqlite3_column_decltype_type lazy_sqlite3_column_decltype = nullptr;
static lazy_sqlite3_column_double_type lazy_sqlite3_column_double = nullptr;
static lazy_sqlite3_column_int_type lazy_sqlite3_column_int = nullptr;
static lazy_sqlite3_column_int64_type lazy_sqlite3_column_int64 = nullptr;
static lazy_sqlite3_column_name_type lazy_sqlite3_column_name = nullptr;
static lazy_sqlite3_column_text_type lazy_sqlite3_column_text = nullptr;
static lazy_sqlite3_column_type_type lazy_sqlite3_column_type = nullptr;
static lazy_sqlite3_errmsg_type lazy_sqlite3_errmsg = nullptr;
static lazy_sqlite3_errstr_type lazy_sqlite3_errstr = nullptr;
static lazy_sqlite3_expanded_sql_type lazy_sqlite3_expanded_sql = nullptr;
static lazy_sqlite3_finalize_type lazy_sqlite3_finalize = nullptr;
static lazy_sqlite3_free_type lazy_sqlite3_free = nullptr;
static lazy_sqlite3_get_autocommit_type lazy_sqlite3_get_autocommit = nullptr;
static lazy_sqlite3_open_v2_type lazy_sqlite3_open_v2 = nullptr;
static lazy_sqlite3_prepare_v3_type lazy_sqlite3_prepare_v3 = nullptr;
static lazy_sqlite3_prepare16_v3_type lazy_sqlite3_prepare16_v3 = nullptr;
static lazy_sqlite3_reset_type lazy_sqlite3_reset = nullptr;
static lazy_sqlite3_step_type lazy_sqlite3_step = nullptr;
static lazy_sqlite3_db_config_type lazy_sqlite3_db_config = nullptr;
static lazy_sqlite3_load_extension_type lazy_sqlite3_load_extension = nullptr;
static lazy_sqlite3_malloc64_type lazy_sqlite3_malloc64 = nullptr;
static lazy_sqlite3_serialize_type lazy_sqlite3_serialize = nullptr;
static lazy_sqlite3_deserialize_type lazy_sqlite3_deserialize = nullptr;
static lazy_sqlite3_stmt_readonly_type lazy_sqlite3_stmt_readonly = nullptr;
static lazy_sqlite3_stmt_busy_type lazy_sqlite3_stmt_busy = nullptr;
static lazy_sqlite3_compileoption_used_type lazy_sqlite3_compileoption_used = nullptr;
static lazy_sqlite3_config_type lazy_sqlite3_config = nullptr;
static lazy_sqlite3_extended_result_codes_type lazy_sqlite3_extended_result_codes = nullptr;
static lazy_sqlite3_extended_errcode_type lazy_sqlite3_extended_errcode = nullptr;
static lazy_sqlite3_error_offset_type lazy_sqlite3_error_offset = nullptr;
static lazy_sqlite3_memory_used_type lazy_sqlite3_memory_used = nullptr;
static lazy_sqlite3_bind_parameter_name_type lazy_sqlite3_bind_parameter_name = nullptr;
static lazy_sqlite3_total_changes_type lazy_sqlite3_total_changes = nullptr;
static lazy_sqlite3_last_insert_rowid_type lazy_sqlite3_last_insert_rowid = nullptr;
#else
// On Linux: statically initialize to actual SQLite functions
static lazy_sqlite3_bind_blob_type lazy_sqlite3_bind_blob = sqlite3_bind_blob;
static lazy_sqlite3_bind_double_type lazy_sqlite3_bind_double = sqlite3_bind_double;
static lazy_sqlite3_bind_int_type lazy_sqlite3_bind_int = sqlite3_bind_int;
static lazy_sqlite3_bind_int64_type lazy_sqlite3_bind_int64 = sqlite3_bind_int64;
static lazy_sqlite3_bind_null_type lazy_sqlite3_bind_null = sqlite3_bind_null;
static lazy_sqlite3_bind_parameter_count_type lazy_sqlite3_bind_parameter_count = sqlite3_bind_parameter_count;
static lazy_sqlite3_bind_parameter_index_type lazy_sqlite3_bind_parameter_index = sqlite3_bind_parameter_index;
static lazy_sqlite3_bind_text_type lazy_sqlite3_bind_text = sqlite3_bind_text;
static lazy_sqlite3_bind_text16_type lazy_sqlite3_bind_text16 = sqlite3_bind_text16;
static lazy_sqlite3_changes_type lazy_sqlite3_changes = sqlite3_changes;
static lazy_sqlite3_clear_bindings_type lazy_sqlite3_clear_bindings = sqlite3_clear_bindings;
static lazy_sqlite3_close_v2_type lazy_sqlite3_close_v2 = sqlite3_close_v2;
static lazy_sqlite3_close_type lazy_sqlite3_close = sqlite3_close;
static lazy_sqlite3_file_control_type lazy_sqlite3_file_control = sqlite3_file_control;
static lazy_sqlite3_column_blob_type lazy_sqlite3_column_blob = sqlite3_column_blob;
static lazy_sqlite3_column_bytes_type lazy_sqlite3_column_bytes = sqlite3_column_bytes;
static lazy_sqlite3_column_bytes16_type lazy_sqlite3_column_bytes16 = sqlite3_column_bytes16;
static lazy_sqlite3_column_count_type lazy_sqlite3_column_count = sqlite3_column_count;
static lazy_sqlite3_column_decltype_type lazy_sqlite3_column_decltype = sqlite3_column_decltype;
static lazy_sqlite3_column_double_type lazy_sqlite3_column_double = sqlite3_column_double;
static lazy_sqlite3_column_int_type lazy_sqlite3_column_int = sqlite3_column_int;
static lazy_sqlite3_column_int64_type lazy_sqlite3_column_int64 = sqlite3_column_int64;
static lazy_sqlite3_column_name_type lazy_sqlite3_column_name = sqlite3_column_name;
static lazy_sqlite3_column_text_type lazy_sqlite3_column_text = sqlite3_column_text;
static lazy_sqlite3_column_type_type lazy_sqlite3_column_type = sqlite3_column_type;
static lazy_sqlite3_errmsg_type lazy_sqlite3_errmsg = sqlite3_errmsg;
static lazy_sqlite3_errstr_type lazy_sqlite3_errstr = sqlite3_errstr;
static lazy_sqlite3_expanded_sql_type lazy_sqlite3_expanded_sql = sqlite3_expanded_sql;
static lazy_sqlite3_finalize_type lazy_sqlite3_finalize = sqlite3_finalize;
static lazy_sqlite3_free_type lazy_sqlite3_free = sqlite3_free;
static lazy_sqlite3_get_autocommit_type lazy_sqlite3_get_autocommit = sqlite3_get_autocommit;
static lazy_sqlite3_open_v2_type lazy_sqlite3_open_v2 = sqlite3_open_v2;
static lazy_sqlite3_prepare_v3_type lazy_sqlite3_prepare_v3 = sqlite3_prepare_v3;
static lazy_sqlite3_prepare16_v3_type lazy_sqlite3_prepare16_v3 = sqlite3_prepare16_v3;
static lazy_sqlite3_reset_type lazy_sqlite3_reset = sqlite3_reset;
static lazy_sqlite3_step_type lazy_sqlite3_step = sqlite3_step;
static lazy_sqlite3_db_config_type lazy_sqlite3_db_config = sqlite3_db_config;
static lazy_sqlite3_load_extension_type lazy_sqlite3_load_extension = sqlite3_load_extension;
static lazy_sqlite3_malloc64_type lazy_sqlite3_malloc64 = sqlite3_malloc64;
static lazy_sqlite3_serialize_type lazy_sqlite3_serialize = sqlite3_serialize;
static lazy_sqlite3_deserialize_type lazy_sqlite3_deserialize = sqlite3_deserialize;
static lazy_sqlite3_stmt_readonly_type lazy_sqlite3_stmt_readonly = sqlite3_stmt_readonly;
static lazy_sqlite3_stmt_busy_type lazy_sqlite3_stmt_busy = sqlite3_stmt_busy;
static lazy_sqlite3_compileoption_used_type lazy_sqlite3_compileoption_used = sqlite3_compileoption_used;
static lazy_sqlite3_config_type lazy_sqlite3_config = sqlite3_config;
static lazy_sqlite3_extended_result_codes_type lazy_sqlite3_extended_result_codes = sqlite3_extended_result_codes;
static lazy_sqlite3_extended_errcode_type lazy_sqlite3_extended_errcode = sqlite3_extended_errcode;
static lazy_sqlite3_error_offset_type lazy_sqlite3_error_offset = sqlite3_error_offset;
static lazy_sqlite3_memory_used_type lazy_sqlite3_memory_used = sqlite3_memory_used;
static lazy_sqlite3_bind_parameter_name_type lazy_sqlite3_bind_parameter_name = sqlite3_bind_parameter_name;
static lazy_sqlite3_total_changes_type lazy_sqlite3_total_changes = sqlite3_total_changes;
static lazy_sqlite3_last_insert_rowid_type lazy_sqlite3_last_insert_rowid = sqlite3_last_insert_rowid;
#endif
// Always redirect through function pointers for consistent behavior
// On macOS: pointers loaded from dylib
// On Linux: pointers initialized to static symbols (or dylib if setCustomSQLite used)
#define sqlite3_bind_blob lazy_sqlite3_bind_blob
#define sqlite3_bind_double lazy_sqlite3_bind_double
#define sqlite3_bind_int lazy_sqlite3_bind_int
@@ -210,24 +270,55 @@ static const char* dlerror()
#define dlsym GetProcAddress
#endif
#include <string>
#if OS(WINDOWS)
static const char* sqlite3_lib_path = "sqlite3.dll";
#define DEFAULT_SQLITE_LIB_PATH "sqlite3.dll"
#elif OS(DARWIN)
static const char* sqlite3_lib_path = "libsqlite3.dylib";
#define DEFAULT_SQLITE_LIB_PATH "libsqlite3.dylib"
#else
static const char* sqlite3_lib_path = "sqlite3";
#define DEFAULT_SQLITE_LIB_PATH "libsqlite3.so"
#endif
static std::string* _user_overriden_sqlite3_lib_path = nullptr;
static void setSQLiteLibPath(const std::string& path)
{
if (path.empty()) {
// This is a memory leak of a value that should only ever be set once per application.
std::exchange(_user_overriden_sqlite3_lib_path, nullptr);
return;
}
std::string* str = new std::string(path);
std::exchange(_user_overriden_sqlite3_lib_path, str);
}
static const std::string& getSQLiteLibPath()
{
static const constexpr std::string _default_sqlite3_lib_path = std::string(DEFAULT_SQLITE_LIB_PATH);
return _user_overriden_sqlite3_lib_path ? *_user_overriden_sqlite3_lib_path : _default_sqlite3_lib_path;
}
static HMODULE sqlite3_handle = nullptr;
#if OS(DARWIN)
static constexpr bool use_static_sqlite = false; // Use dynamic linking by default on macOS
#else
static constexpr bool use_static_sqlite = true; // Use static linking by default on Linux/Windows
#endif
static int lazyLoadSQLite()
{
if (sqlite3_handle)
return 0;
if (use_static_sqlite && _user_overriden_sqlite3_lib_path == nullptr) {
return 0;
}
#if OS(WINDOWS)
sqlite3_handle = LoadLibraryA(sqlite3_lib_path);
sqlite3_handle = LoadLibraryA(getSQLiteLibPath().c_str());
#else
sqlite3_handle = dlopen(sqlite3_lib_path, RTLD_LAZY);
sqlite3_handle = dlopen(getSQLiteLibPath().c_str(), RTLD_LAZY);
#endif
if (!sqlite3_handle) {
@@ -305,7 +396,7 @@ static int lazyLoadSQLite()
}
if (!lazy_sqlite3_memory_used) {
lazy_sqlite3_memory_used = []() -> int64_t {
lazy_sqlite3_memory_used = []() -> sqlite3_int64 {
return 0;
};
}

View File

@@ -0,0 +1,101 @@
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
describe("SQLite custom loading", () => {
test("default SQLite loads successfully", () => {
// Create a new process to ensure clean state
const code = `
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
const result = db.query("SELECT sqlite_version() as version").get();
console.log(result.version);
db.close();
`;
const proc = Bun.spawnSync({
cmd: [bunExe(), "-e", code],
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
expect(proc.exitCode).toBe(0);
expect(proc.stdout.toString().trim()).toMatch(/^\d+\.\d+\.\d+$/);
});
test("setCustomSQLite throws error after SQLite is already loaded", () => {
// Create a new process to ensure clean state
const code = `
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
db.close();
try {
Database.setCustomSQLite("/usr/lib/libsqlite3.so");
console.log("ERROR: Should have thrown");
process.exit(1);
} catch (error) {
console.log("SUCCESS");
}
`;
const proc = Bun.spawnSync({
cmd: [bunExe(), "-e", code],
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
expect(proc.exitCode).toBe(0);
expect(proc.stdout.toString().trim()).toBe("SUCCESS");
});
// This test is only meaningful on systems with a separate SQLite library
test.todoIf(process.platform === "linux", "setCustomSQLite can load dynamic library before first use", () => {
// This test would require a known SQLite library path
// and needs to run in a fresh process
const code = `
import { Database } from "bun:sqlite";
import { existsSync } from "fs";
const paths = [
"/usr/lib/x86_64-linux-gnu/libsqlite3.so",
"/usr/lib/aarch64-linux-gnu/libsqlite3.so",
"/usr/lib/libsqlite3.so",
];
let customPath = null;
for (const path of paths) {
if (existsSync(path)) {
customPath = path;
break;
}
}
if (customPath) {
Database.setCustomSQLite(customPath);
const db = new Database(":memory:");
const result = db.query("SELECT sqlite_version() as version").get();
console.log(result.version);
db.close();
} else {
console.log("NO_LIBRARY");
}
`;
const proc = Bun.spawnSync({
cmd: [bunExe(), "-e", code],
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
expect(proc.exitCode).toBe(0);
const output = proc.stdout.toString().trim();
if (output === "NO_LIBRARY") {
// Skip if no library found
return;
}
expect(output).toMatch(/^\d+\.\d+\.\d+$/);
});
});