Skip to content

Commit 89cc8cd

Browse files
authored
Merge pull request #1922 from tursodatabase/glauber/sqlean
bundle SQLean extensions
2 parents 5b8934e + d3a156c commit 89cc8cd

File tree

143 files changed

+67523
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+67523
-4
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libsql-ffi/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ libsql-wasmtime-bindings = { version = "0.2.1", optional = true }
1616
[build-dependencies]
1717
bindgen = "0.66.1"
1818
cc = "1.0"
19+
glob = "0.3"
1920

2021
[features]
2122
session = []
@@ -28,3 +29,17 @@ wasm32-wasi-vfs = []
2829
unlock_notify = []
2930
preupdate_hook = []
3031
sqlcipher = []
32+
sqlean-extension-uuid = []
33+
sqlean-extension-crypto = []
34+
sqlean-extension-fuzzy = []
35+
sqlean-extension-math = []
36+
sqlean-extension-stats = []
37+
sqlean-extension-text = []
38+
sqlean-extensions = [
39+
"sqlean-extension-uuid",
40+
"sqlean-extension-crypto",
41+
"sqlean-extension-fuzzy",
42+
"sqlean-extension-math",
43+
"sqlean-extension-stats",
44+
"sqlean-extension-text"
45+
]

libsql-ffi/build.rs

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use glob::glob;
12
use std::env;
23
use std::ffi::OsString;
34
use std::fs::{self, OpenOptions};
@@ -130,6 +131,57 @@ fn make_amalgamation() {
130131
.unwrap();
131132
}
132133

134+
fn generate_sqlean(enabled_extensions: &[&str], output_path: &Path) -> io::Result<()> {
135+
let mut content = String::from(
136+
r#"// Generated by build.rs
137+
138+
139+
#include "sqlite3.c"
140+
SQLITE_EXTENSION_INIT1
141+
142+
"#,
143+
);
144+
145+
for ext in enabled_extensions {
146+
content.push_str(&format!("#include \"{}/extension.h\"\n", ext));
147+
}
148+
149+
content.push_str(
150+
r#"
151+
#include "sqlean.h"
152+
153+
static void sqlean_version(sqlite3_context* context, int argc, sqlite3_value** argv) {
154+
sqlite3_result_text(context, SQLEAN_VERSION, -1, SQLITE_STATIC);
155+
}
156+
157+
#ifdef _WIN32
158+
__declspec(dllexport)
159+
#endif
160+
int sqlite3_sqlean_init(sqlite3* db, char** errmsg_ptr, const sqlite3_api_routines* api) {
161+
(void)errmsg_ptr;
162+
SQLITE_EXTENSION_INIT2(api);
163+
static const int flags = SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC;
164+
sqlite3_create_function(db, "sqlean_version", 0, flags, 0, sqlean_version, 0, 0);
165+
"#,
166+
);
167+
168+
for ext in enabled_extensions {
169+
content.push_str(&format!(" {}_init(db);\n", ext));
170+
}
171+
172+
content.push_str(
173+
r#" return SQLITE_OK;
174+
}
175+
176+
int core_init(const char* dummy) {
177+
return sqlite3_auto_extension((void*)sqlite3_sqlean_init);
178+
}
179+
"#,
180+
);
181+
182+
std::fs::write(output_path, content)
183+
}
184+
133185
pub fn build_bundled(out_dir: &str, out_path: &Path) {
134186
let bindgen_rs_path = if cfg!(feature = "session") {
135187
"bundled/bindings/session_bindgen.rs"
@@ -146,8 +198,7 @@ pub fn build_bundled(out_dir: &str, out_path: &Path) {
146198
std::fs::copy(format!("{dir}/{bindgen_rs_path}"), out_path).unwrap();
147199

148200
let mut cfg = cc::Build::new();
149-
cfg.file(format!("{BUNDLED_DIR}/src/sqlite3.c"))
150-
.flag("-std=c11")
201+
cfg.flag("-std=c11")
151202
.flag("-DSQLITE_CORE")
152203
.flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
153204
.flag("-DSQLITE_ENABLE_API_ARMOR")
@@ -169,6 +220,62 @@ pub fn build_bundled(out_dir: &str, out_path: &Path) {
169220
.flag("-D_POSIX_THREAD_SAFE_FUNCTIONS") // cross compile with MinGW
170221
.warnings(false);
171222

223+
let mut sqlean_patterns = vec![];
224+
let mut enabled_extensions = Vec::new();
225+
226+
if cfg!(feature = "sqlean-extension-crypto") {
227+
enabled_extensions.push("crypto");
228+
sqlean_patterns.push("crypto/*.c");
229+
}
230+
231+
if cfg!(feature = "sqlean-extension-fuzzy") {
232+
enabled_extensions.push("fuzzy");
233+
sqlean_patterns.push("fuzzy/*.c");
234+
}
235+
236+
if cfg!(feature = "sqlean-extension-math") {
237+
enabled_extensions.push("math");
238+
sqlean_patterns.push("math/*.c");
239+
}
240+
241+
if cfg!(feature = "sqlean-extension-stats") {
242+
enabled_extensions.push("stats");
243+
sqlean_patterns.push("stats/*.c");
244+
}
245+
246+
if cfg!(feature = "sqlean-extension-text") {
247+
enabled_extensions.push("text");
248+
sqlean_patterns.push("text/*.c");
249+
sqlean_patterns.push("text/*/*.c");
250+
}
251+
252+
if cfg!(feature = "sqlean-extension-uuid") {
253+
enabled_extensions.push("uuid");
254+
sqlean_patterns.push("uuid/*.c");
255+
}
256+
257+
if sqlean_patterns.is_empty() {
258+
cfg.file(format!("{BUNDLED_DIR}/src/sqlite3.c"));
259+
} else {
260+
cfg.flag("-DSQLITE_EXTRA_INIT=core_init");
261+
262+
let mut sqlean_sources = Vec::new();
263+
for pattern in sqlean_patterns {
264+
let full_pattern = format!("{BUNDLED_DIR}/sqlean/{}", pattern);
265+
sqlean_sources.extend(glob(&full_pattern).unwrap().filter_map(Result::ok));
266+
}
267+
268+
cfg.files(sqlean_sources);
269+
270+
let sqlean = Path::new(BUNDLED_DIR)
271+
.join("src")
272+
.join("sqlite3-sqlean-generated.c");
273+
generate_sqlean(&enabled_extensions, &sqlean).unwrap();
274+
cfg.file(&sqlean);
275+
276+
cfg.include(format!("{BUNDLED_DIR}/sqlean/"));
277+
}
278+
172279
if cfg!(feature = "wasmtime-bindings") {
173280
cfg.flag("-DLIBSQL_ENABLE_WASM_RUNTIME=1");
174281
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) 2023 Anton Zhiyanov, MIT License
2+
// https://github.com/nalgeon/sqlean
3+
4+
// Base32 encoding/decoding (RFC 4648)
5+
6+
#include <stdint.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
10+
static const char base32_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
11+
12+
uint8_t* base32_encode(const uint8_t* src, size_t len, size_t* out_len) {
13+
*out_len = ((len + 4) / 5) * 8;
14+
uint8_t* encoded = malloc(*out_len + 1);
15+
if (encoded == NULL) {
16+
*out_len = 0;
17+
return NULL;
18+
}
19+
20+
for (size_t i = 0, j = 0; i < len;) {
21+
uint32_t octet0 = i < len ? src[i++] : 0;
22+
uint32_t octet1 = i < len ? src[i++] : 0;
23+
uint32_t octet2 = i < len ? src[i++] : 0;
24+
uint32_t octet3 = i < len ? src[i++] : 0;
25+
uint32_t octet4 = i < len ? src[i++] : 0;
26+
27+
encoded[j++] = base32_chars[octet0 >> 3];
28+
encoded[j++] = base32_chars[((octet0 & 0x07) << 2) | (octet1 >> 6)];
29+
encoded[j++] = base32_chars[(octet1 >> 1) & 0x1F];
30+
encoded[j++] = base32_chars[((octet1 & 0x01) << 4) | (octet2 >> 4)];
31+
encoded[j++] = base32_chars[((octet2 & 0x0F) << 1) | (octet3 >> 7)];
32+
encoded[j++] = base32_chars[(octet3 >> 2) & 0x1F];
33+
encoded[j++] = base32_chars[((octet3 & 0x03) << 3) | (octet4 >> 5)];
34+
encoded[j++] = base32_chars[octet4 & 0x1F];
35+
}
36+
37+
if (len % 5 != 0) {
38+
size_t padding = 7 - (len % 5) * 8 / 5;
39+
for (size_t i = 0; i < padding; i++) {
40+
encoded[*out_len - padding + i] = '=';
41+
}
42+
}
43+
44+
encoded[*out_len] = '\0';
45+
return encoded;
46+
}
47+
48+
uint8_t* base32_decode(const uint8_t* src, size_t len, size_t* out_len) {
49+
while (len > 0 && src[len - 1] == '=') {
50+
len--;
51+
}
52+
*out_len = len * 5 / 8;
53+
uint8_t* decoded = malloc(*out_len);
54+
if (decoded == NULL) {
55+
*out_len = 0;
56+
return NULL;
57+
}
58+
59+
size_t bits = 0, value = 0, count = 0;
60+
for (size_t i = 0; i < len; i++) {
61+
uint8_t c = src[i];
62+
if (c >= 'A' && c <= 'Z') {
63+
c -= 'A';
64+
} else if (c >= '2' && c <= '7') {
65+
c -= '2' - 26;
66+
} else {
67+
continue;
68+
}
69+
value = (value << 5) | c;
70+
bits += 5;
71+
if (bits >= 8) {
72+
decoded[count++] = (uint8_t)(value >> (bits - 8));
73+
bits -= 8;
74+
}
75+
}
76+
if (bits >= 5 || (value & ((1 << bits) - 1)) != 0) {
77+
free(decoded);
78+
return NULL;
79+
}
80+
*out_len = count;
81+
return decoded;
82+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2023 Anton Zhiyanov, MIT License
2+
// https://github.com/nalgeon/sqlean
3+
4+
// Base32 encoding/decoding (RFC 4648)
5+
6+
#ifndef _BASE32_H_
7+
#define _BASE32_H_
8+
9+
#include <stdint.h>
10+
11+
uint8_t* base32_encode(const uint8_t* src, size_t len, size_t* out_len);
12+
uint8_t* base32_decode(const uint8_t* src, size_t len, size_t* out_len);
13+
14+
#endif /* _BASE32_H_ */
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) 2023 Anton Zhiyanov, MIT License
2+
// https://github.com/nalgeon/sqlean
3+
4+
// Base64 encoding/decoding (RFC 4648)
5+
6+
#include <stdint.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
10+
static const char base64_chars[] =
11+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12+
13+
uint8_t* base64_encode(const uint8_t* src, size_t len, size_t* out_len) {
14+
uint8_t* encoded = NULL;
15+
size_t i, j;
16+
uint32_t octets;
17+
18+
*out_len = ((len + 2) / 3) * 4;
19+
encoded = malloc(*out_len + 1);
20+
if (encoded == NULL) {
21+
*out_len = 0;
22+
return NULL;
23+
}
24+
25+
for (i = 0, j = 0; i < len; i += 3, j += 4) {
26+
octets =
27+
(src[i] << 16) | ((i + 1 < len ? src[i + 1] : 0) << 8) | (i + 2 < len ? src[i + 2] : 0);
28+
encoded[j] = base64_chars[(octets >> 18) & 0x3f];
29+
encoded[j + 1] = base64_chars[(octets >> 12) & 0x3f];
30+
encoded[j + 2] = base64_chars[(octets >> 6) & 0x3f];
31+
encoded[j + 3] = base64_chars[octets & 0x3f];
32+
}
33+
34+
if (len % 3 == 1) {
35+
encoded[*out_len - 1] = '=';
36+
encoded[*out_len - 2] = '=';
37+
} else if (len % 3 == 2) {
38+
encoded[*out_len - 1] = '=';
39+
}
40+
41+
encoded[*out_len] = '\0';
42+
return encoded;
43+
}
44+
45+
static const uint8_t base64_table[] = {
46+
// Map base64 characters to their corresponding values
47+
['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7,
48+
['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15,
49+
['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
50+
['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31,
51+
['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39,
52+
['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47,
53+
['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55,
54+
['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63,
55+
};
56+
57+
uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len) {
58+
if (len % 4 != 0) {
59+
return NULL;
60+
}
61+
62+
size_t padding = 0;
63+
if (src[len - 1] == '=') {
64+
padding++;
65+
}
66+
if (src[len - 2] == '=') {
67+
padding++;
68+
}
69+
70+
*out_len = (len / 4) * 3 - padding;
71+
uint8_t* decoded = malloc(*out_len);
72+
if (decoded == NULL) {
73+
*out_len = 0;
74+
return NULL;
75+
}
76+
77+
for (size_t i = 0, j = 0; i < len; i += 4, j += 3) {
78+
uint32_t block = 0;
79+
for (size_t k = 0; k < 4; k++) {
80+
block <<= 6;
81+
if (src[i + k] == '=') {
82+
padding--;
83+
} else {
84+
uint8_t index = base64_table[src[i + k]];
85+
if (index == 0 && src[i + k] != 'A') {
86+
free(decoded);
87+
return NULL;
88+
}
89+
block |= index;
90+
}
91+
}
92+
93+
decoded[j] = (block >> 16) & 0xFF;
94+
if (j + 1 < *out_len) {
95+
decoded[j + 1] = (block >> 8) & 0xFF;
96+
}
97+
if (j + 2 < *out_len) {
98+
decoded[j + 2] = block & 0xFF;
99+
}
100+
}
101+
102+
return decoded;
103+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) 2023 Anton Zhiyanov, MIT License
2+
// https://github.com/nalgeon/sqlean
3+
4+
// Base64 encoding/decoding (RFC 4648)
5+
6+
#ifndef BASE64_H
7+
#define BASE64_H
8+
9+
#include <stddef.h>
10+
#include <stdint.h>
11+
12+
uint8_t* base64_encode(const uint8_t* src, size_t len, size_t* out_len);
13+
uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len);
14+
15+
#endif /* BASE64_H */

0 commit comments

Comments
 (0)