Skip to content

Commit 7f80905

Browse files
committed
Fix a bug in loadExtension and add back the loadExtension test
1 parent 9b6c697 commit 7f80905

File tree

3 files changed

+248
-19
lines changed

3 files changed

+248
-19
lines changed

src/database.cc

+1
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* dat
709709
EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception);
710710

711711
baton->deferred.Reject(exception);
712+
return;
712713
}
713714

714715
db->Process();

test/extension.test.js

+14-19
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
1-
// let sqlite3 = require('..');
2-
// let assert = require('assert');
3-
// let exists = require('fs').existsSync || require('path').existsSync;
1+
const { join } = require('node:path');
2+
const sqlite3 = require('..');
3+
const { cwd } = require('node:process');
4+
const { exec: execCb } = require('node:child_process');
5+
const {promisify} = require('node:util');
46

5-
/*
7+
const exec = promisify(execCb);
68

7-
// disabled because this is not a generically safe test to run on all systems
9+
const uuidExtension = join(cwd(), 'test', 'support', 'uuid.c');
810

9-
var spatialite_ext = '/usr/local/lib/libspatialite.dylib';
1011

11-
describe('loadExtension', function(done) {
12-
var db;
13-
before(function(done) {
14-
db = new sqlite3.Database(':memory:', done);
12+
describe('loadExtension', function() {
13+
before(async function() {
14+
await exec(`gcc -g -fPIC -shared ${uuidExtension} -o ${uuidExtension}.so`);
1515
});
1616

17-
if (exists(spatialite_ext)) {
18-
it('libspatialite', function(done) {
19-
db.loadExtension(spatialite_ext, done);
20-
});
21-
} else {
22-
it('libspatialite');
23-
}
17+
it('can load the uuid extension', async function() {
18+
const db = await sqlite3.Database.create(':memory:');
19+
await db.loadExtension(uuidExtension);
20+
});
2421
});
25-
26-
*/

test/support/uuid.c

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/*
2+
** 2019-10-23
3+
**
4+
** The author disclaims copyright to this source code. In place of
5+
** a legal notice, here is a blessing:
6+
**
7+
** May you do good and not evil.
8+
** May you find forgiveness for yourself and forgive others.
9+
** May you share freely, never taking more than you give.
10+
**
11+
******************************************************************************
12+
**
13+
** This SQLite extension implements functions that handling RFC-4122 UUIDs
14+
** Three SQL functions are implemented:
15+
**
16+
** uuid() - generate a version 4 UUID as a string
17+
** uuid_str(X) - convert a UUID X into a well-formed UUID string
18+
** uuid_blob(X) - convert a UUID X into a 16-byte blob
19+
**
20+
** The output from uuid() and uuid_str(X) are always well-formed RFC-4122
21+
** UUID strings in this format:
22+
**
23+
** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
24+
**
25+
** All of the 'x', 'M', and 'N' values are lower-case hexadecimal digits.
26+
** The M digit indicates the "version". For uuid()-generated UUIDs, the
27+
** version is always "4" (a random UUID). The upper three bits of N digit
28+
** are the "variant". This library only supports variant 1 (indicated
29+
** by values of N between '8' and 'b') as those are overwhelming the most
30+
** common. Other variants are for legacy compatibility only.
31+
**
32+
** The output of uuid_blob(X) is always a 16-byte blob. The UUID input
33+
** string is converted in network byte order (big-endian) in accordance
34+
** with RFC-4122 specifications for variant-1 UUIDs. Note that network
35+
** byte order is *always* used, even if the input self-identifies as a
36+
** variant-2 UUID.
37+
**
38+
** The input X to the uuid_str() and uuid_blob() functions can be either
39+
** a string or a BLOB. If it is a BLOB it must be exactly 16 bytes in
40+
** length or else a NULL is returned. If the input is a string it must
41+
** consist of 32 hexadecimal digits, upper or lower case, optionally
42+
** surrounded by {...} and with optional "-" characters interposed in the
43+
** middle. The flexibility of input is inspired by the PostgreSQL
44+
** implementation of UUID functions that accept in all of the following
45+
** formats:
46+
**
47+
** A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
48+
** {a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
49+
** a0eebc999c0b4ef8bb6d6bb9bd380a11
50+
** a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
51+
** {a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}
52+
**
53+
** If any of the above inputs are passed into uuid_str(), the output will
54+
** always be in the canonical RFC-4122 format:
55+
**
56+
** a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
57+
**
58+
** If the X input string has too few or too many digits or contains
59+
** stray characters other than {, }, or -, then NULL is returned.
60+
*/
61+
#include "sqlite3ext.h"
62+
SQLITE_EXTENSION_INIT1
63+
#include <assert.h>
64+
#include <string.h>
65+
#include <ctype.h>
66+
67+
#if !defined(SQLITE_ASCII) && !defined(SQLITE_EBCDIC)
68+
# define SQLITE_ASCII 1
69+
#endif
70+
71+
/*
72+
** Translate a single byte of Hex into an integer.
73+
** This routine only works if h really is a valid hexadecimal
74+
** character: 0..9a..fA..F
75+
*/
76+
static unsigned char sqlite3UuidHexToInt(int h){
77+
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
78+
#ifdef SQLITE_ASCII
79+
h += 9*(1&(h>>6));
80+
#endif
81+
#ifdef SQLITE_EBCDIC
82+
h += 9*(1&~(h>>4));
83+
#endif
84+
return (unsigned char)(h & 0xf);
85+
}
86+
87+
/*
88+
** Convert a 16-byte BLOB into a well-formed RFC-4122 UUID. The output
89+
** buffer zStr should be at least 37 bytes in length. The output will
90+
** be zero-terminated.
91+
*/
92+
static void sqlite3UuidBlobToStr(
93+
const unsigned char *aBlob, /* Input blob */
94+
unsigned char *zStr /* Write the answer here */
95+
){
96+
static const char zDigits[] = "0123456789abcdef";
97+
int i, k;
98+
unsigned char x;
99+
k = 0;
100+
for(i=0, k=0x550; i<16; i++, k=k>>1){
101+
if( k&1 ){
102+
zStr[0] = '-';
103+
zStr++;
104+
}
105+
x = aBlob[i];
106+
zStr[0] = zDigits[x>>4];
107+
zStr[1] = zDigits[x&0xf];
108+
zStr += 2;
109+
}
110+
*zStr = 0;
111+
}
112+
113+
/*
114+
** Attempt to parse a zero-terminated input string zStr into a binary
115+
** UUID. Return 0 on success, or non-zero if the input string is not
116+
** parsable.
117+
*/
118+
static int sqlite3UuidStrToBlob(
119+
const unsigned char *zStr, /* Input string */
120+
unsigned char *aBlob /* Write results here */
121+
){
122+
int i;
123+
if( zStr[0]=='{' ) zStr++;
124+
for(i=0; i<16; i++){
125+
if( zStr[0]=='-' ) zStr++;
126+
if( isxdigit(zStr[0]) && isxdigit(zStr[1]) ){
127+
aBlob[i] = (sqlite3UuidHexToInt(zStr[0])<<4)
128+
+ sqlite3UuidHexToInt(zStr[1]);
129+
zStr += 2;
130+
}else{
131+
return 1;
132+
}
133+
}
134+
if( zStr[0]=='}' ) zStr++;
135+
return zStr[0]!=0;
136+
}
137+
138+
/*
139+
** Render sqlite3_value pIn as a 16-byte UUID blob. Return a pointer
140+
** to the blob, or NULL if the input is not well-formed.
141+
*/
142+
static const unsigned char *sqlite3UuidInputToBlob(
143+
sqlite3_value *pIn, /* Input text */
144+
unsigned char *pBuf /* output buffer */
145+
){
146+
switch( sqlite3_value_type(pIn) ){
147+
case SQLITE_TEXT: {
148+
const unsigned char *z = sqlite3_value_text(pIn);
149+
if( sqlite3UuidStrToBlob(z, pBuf) ) return 0;
150+
return pBuf;
151+
}
152+
case SQLITE_BLOB: {
153+
int n = sqlite3_value_bytes(pIn);
154+
return n==16 ? sqlite3_value_blob(pIn) : 0;
155+
}
156+
default: {
157+
return 0;
158+
}
159+
}
160+
}
161+
162+
/* Implementation of uuid() */
163+
static void sqlite3UuidFunc(
164+
sqlite3_context *context,
165+
int argc,
166+
sqlite3_value **argv
167+
){
168+
unsigned char aBlob[16];
169+
unsigned char zStr[37];
170+
(void)argc;
171+
(void)argv;
172+
sqlite3_randomness(16, aBlob);
173+
aBlob[6] = (aBlob[6]&0x0f) + 0x40;
174+
aBlob[8] = (aBlob[8]&0x3f) + 0x80;
175+
sqlite3UuidBlobToStr(aBlob, zStr);
176+
sqlite3_result_text(context, (char*)zStr, 36, SQLITE_TRANSIENT);
177+
}
178+
179+
/* Implementation of uuid_str() */
180+
static void sqlite3UuidStrFunc(
181+
sqlite3_context *context,
182+
int argc,
183+
sqlite3_value **argv
184+
){
185+
unsigned char aBlob[16];
186+
unsigned char zStr[37];
187+
const unsigned char *pBlob;
188+
(void)argc;
189+
pBlob = sqlite3UuidInputToBlob(argv[0], aBlob);
190+
if( pBlob==0 ) return;
191+
sqlite3UuidBlobToStr(pBlob, zStr);
192+
sqlite3_result_text(context, (char*)zStr, 36, SQLITE_TRANSIENT);
193+
}
194+
195+
/* Implementation of uuid_blob() */
196+
static void sqlite3UuidBlobFunc(
197+
sqlite3_context *context,
198+
int argc,
199+
sqlite3_value **argv
200+
){
201+
unsigned char aBlob[16];
202+
const unsigned char *pBlob;
203+
(void)argc;
204+
pBlob = sqlite3UuidInputToBlob(argv[0], aBlob);
205+
if( pBlob==0 ) return;
206+
sqlite3_result_blob(context, pBlob, 16, SQLITE_TRANSIENT);
207+
}
208+
209+
#ifdef _WIN32
210+
__declspec(dllexport)
211+
#endif
212+
int sqlite3_uuid_init(
213+
sqlite3 *db,
214+
char **pzErrMsg,
215+
const sqlite3_api_routines *pApi
216+
){
217+
int rc = SQLITE_OK;
218+
SQLITE_EXTENSION_INIT2(pApi);
219+
(void)pzErrMsg; /* Unused parameter */
220+
rc = sqlite3_create_function(db, "uuid", 0, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
221+
sqlite3UuidFunc, 0, 0);
222+
if( rc==SQLITE_OK ){
223+
rc = sqlite3_create_function(db, "uuid_str", 1,
224+
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
225+
0, sqlite3UuidStrFunc, 0, 0);
226+
}
227+
if( rc==SQLITE_OK ){
228+
rc = sqlite3_create_function(db, "uuid_blob", 1,
229+
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
230+
0, sqlite3UuidBlobFunc, 0, 0);
231+
}
232+
return rc;
233+
}

0 commit comments

Comments
 (0)