├── LICENSE
├── Makefile
├── README.md
├── sqlitejs.mak
└── src
└── sqlitejs.c
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015-2017, Abilio Marques
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
24 | The views and conclusions contained in the software and documentation are those
25 | of the authors and should not be interpreted as representing official policies,
26 | either expressed or implied, of the FreeBSD Project.
27 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # To build this, download and extract duktape
2 | #
3 | # extract the content of duktape source code tar. You must end with a directory called duktape-DUKTAPE_VERSION (e.g.,: duktape-2.3.0)
4 | #
5 | # To build this on FreeBSD install sqlite3
6 | #
7 | # on Linux you must install:
8 | # libsqlite3-dev or similar
9 |
10 |
11 | SQLITE_FLAGS=`pkg-config --cflags --silence-errors sqlite3`
12 | COMPILE_SWITCHES=-std=c99 -O3 -fPIC
13 | DUKTAPE_LIB != ls -d duktape-?.?.?
14 |
15 | all: js.so
16 |
17 | js.so: sqlitejs.o duktape.o
18 | cc -shared -o js.so sqlitejs.o duktape.o -lm
19 | @[ "`uname -s`" == "Darwin" ] && mv js.so js.dylib || :
20 |
21 | sqlitejs.o: src/sqlitejs.c
22 | cc -c $(COMPILE_SWITCHES) $(SQLITE_FLAGS) -I$(DUKTAPE_LIB)/src/ src/sqlitejs.c
23 |
24 | duktape.o:
25 | cc -c $(COMPILE_SWITCHES) $(DUKTAPE_LIB)/src/duktape.c
26 |
27 | clean:
28 | @rm *.o
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SQLite's Cafe: JavaScript for SQLite
2 |
3 | Create new SQL functions! Write them using JavaScript.
4 |
5 |
6 | # Usage
7 |
8 | This is an [SQLite](http://sqlite.org/) plugin. After loading it, you can use the function ```createjs``` to define your own functions, like this:
9 |
10 | ```
11 | SELECT createjs({parameters});
12 | ```
13 |
14 | This query will return ```ok``` if everything went fine.
15 |
16 | SQL supports two types of functions:
17 | * Scalar: returns a result for each record.
18 | * Aggregate: process all records, then return a single final result.
19 |
20 |
21 | ## Scalar
22 |
23 | When creating a scalar function, you must provide 2 parameters, in the following order:
24 | * Name: the function you are defining.
25 | * Code: well... the JavaScript code. Returns the result.
26 |
27 | For example, to create a function that calculates the cosine of an angle:
28 | ```
29 | SELECT createjs('cos', 'Math.cos(arg[0])');
30 | ```
31 |
32 | And now you can calculate cosines on a query:
33 | ```
34 | CREATE TABLE t(angle NUMERIC);
35 | INSERT INTO t(angle) VALUES (0),(1.571),(3.1416);
36 |
37 | SELECT cos(angle) FROM t; -- should return approximately {1, 0, -1}
38 | ```
39 |
40 |
41 | ## Aggregate
42 |
43 | When creating aggregate functions, you must provide 4 parameters, in the following order:
44 | * Name: the function you are defining.
45 | * Init: this code will execute before the first record is processed.
46 | * Step: code called on each record.
47 | * Final: code for the last step after all records have been processed. Returns the result.
48 |
49 | For example, to create a function that calculates the [geometric mean](https://en.wikipedia.org/wiki/Geometric_mean) of a set of numbers:
50 |
51 | ```
52 | SELECT createjs('gmean',
53 | 'prod = 1; n = 0;',
54 | 'n++; prod = prod * arg[0];',
55 | 'Math.pow(prod, 1/n)');
56 | ```
57 |
58 | And now, used on a query:
59 |
60 | ```
61 | CREATE TABLE data(val NUMERIC);
62 | INSERT INTO data(val) VALUES (2), (4), (8);
63 |
64 | SELECT gmean(val) FROM data; -- should return 4
65 | ```
66 |
67 |
68 | # Parameters
69 |
70 | Parameters supplied to the functions can be accessed from within JavaScript. The array: ```arg[i]``` contains the values. ```arg``` is a zero-based array (0 <= i < n), like all JavaScript arrays.
71 |
72 | Example:
73 | ```
74 | -- this is a pattern matching example using JavaScript's internal function
75 | SELECT createjs('regex', 'arg[0].match(arg[1])[arg[2]]');
76 |
77 | SELECT regex('abc 24 ef', '(\w+) (\d+)', 1); -- Should return abc
78 | SELECT regex('abc 24 ef', '(\w+) (\d+)', 2); -- Should return 24
79 | SELECT regex('abc 24 ef', '\d+', 0); -- Should return 24
80 | ```
81 |
82 |
83 | # Returning values
84 |
85 | As seen in the previous examples, the result is returned by leaving the value in the JavaScript machine stack, e.g., ```a = b + c; a;``` will return the result of ```b + c```.
86 |
87 | Functions must always return a value. For aggregates, this is only performed on the final step. Currently, you can't return an array into SQLite.
88 |
89 |
90 | # Reading from files
91 |
92 | You can use the auxiliary function named ```loadfile``` to get the content of a file. This is useful to declare long functions. The signature is:
93 |
94 | ```
95 | loadfile(filename,[type])
96 | ```
97 |
98 | For example, to create a function which source code is stored in a file named ```longcode.js```, do:
99 |
100 | ```
101 | select createjs('longcode', select loadfile('longcode.js'));
102 | ```
103 |
104 | ```loadfile``` can take an optional type parameter. If type is 'b', the file content will be read as a blob. This can be useful for other applications, e.g., reading binary files inside the database.
105 |
106 |
107 | # Notes
108 |
109 | * You can redefine a function at any time by calling ```createjs``` again. This holds true even for SQLite's native functions.
110 |
111 |
112 | # Building
113 |
114 | This plugin uses [Duktape](http://duktape.org/) as the JavaScript machine. You must download their code from http://duktape.org/download.html, and extract it in the plugin base directory. You must end up with a directory called duktape-DUKTAPE_VERSION (ie: duktape-3.2.0).
115 |
116 | On FreeBSD, you must have ```sqlite3``` installed. The Linux equivalent is something like ```libsqlite3-dev``` (Ubuntu) or ```sqlite-devel``` (Fedora). The library names and their locations could be different on other Operating Systems. If that's the case, you may need to edit the Makefile.
117 |
118 | On Windows, using Visual Studio, you can use the provided ```sqlitejs.mak```. You'll need to get [sqlite source code amalgamation](https://www.sqlite.org/download.html) from their site. The files sqlite3.h and sqlite3ext.h must be extracted inside a folder named sqlite. Afterwards, you can compile the dll like this:
119 |
120 | ```
121 | nmake -f sqlitejs.mak
122 | ```
123 |
124 | # Loading
125 |
126 | To use this plugin from within the sqlite3 command line tool, you must run:
127 |
128 | ```.load path-to-plugin/js``` (for security reasons, SQLite needs you to provide the library path if the plugin is not located inside ```LD_LIBRARY_PATH```. Current directory '.' is a valid path, e.g., ```.load ./js```).
129 |
130 | You could also use this from within almost any code that uses sqlite3 as a library. Refer to SQLite website to get more information about ```sqlite3_load_extension``` and ```sqlite3_enable_load_extension``` or look for their equivalents in your language bindings' documentation.
131 |
132 |
133 | ## License
134 |
135 | This code is published under the Simplified BSD License.
136 |
137 | Copyright (c) 2015, Abilio Marques All rights reserved.
138 |
139 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
140 |
141 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
142 |
143 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
144 |
145 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
146 |
147 | The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project.
148 |
--------------------------------------------------------------------------------
/sqlitejs.mak:
--------------------------------------------------------------------------------
1 | # to run this: "nmake -f sqlitejs.mak"
2 | #
3 | # Extract the content of duktape source code tar.
4 | # You must end with a directory called duktape-DUKTAPE_VERSION (ie: duktape-1.5.1).
5 | # Please set the DUKTAPE_VERSION variable accordingly.
6 | #
7 | # Extract sqlite3.h and sqlite3ext.h inside a folder called sqlite.
8 | #
9 | # it should generate js.dll as the output
10 | #
11 | # Note: this Makefile hasn't been tested
12 |
13 | DUKTAPE_VERSION=1.5.1
14 |
15 | CPP=cl.exe
16 |
17 | ALL :
18 | @if not exist ".\sqlite\sqlite3.h" echo "please extract SQLite's header files inside the sqlite folder"
19 | @if not exist ".\sqlite" mkdir sqlite
20 |
21 | $(CPP) /Ot /GD /nologo /W3 /I .\duktape-$(DUKTAPE_VERSION) /I .\sqlite .\duktape-$(DUKTAPE_VERSION)\src\duktape.c .\src\sqlitejs.c -link -dll -out:js.dll
22 | @erase *.obj
23 |
24 |
--------------------------------------------------------------------------------
/src/sqlitejs.c:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * SQLite's Cafe: Javascript for SQLite
4 | *
5 | * Create new SQL functions! Write them using Javascript.
6 | *
7 | * By: Abilio Marques
8 | *
9 | *
10 | * After loading this plugin, you can use the newly function "createjs" to create your own
11 | * functions.
12 | *
13 | * See README.md for more information on how to use it.
14 | *
15 | * This code is published under the Simplified BSD License
16 | *
17 | */
18 |
19 | #include
20 | #include
21 | #include "duktape.h"
22 |
23 |
24 | #include
25 | SQLITE_EXTENSION_INIT1;
26 | #if defined( _WIN32 )
27 | #define _USE_MATH_DEFINES
28 | #endif /* _WIN32 */
29 |
30 |
31 | //
32 | // Get a new Duk heap
33 | //
34 |
35 | static void *createDukHeap(void) {
36 | duk_context *ctx = NULL;
37 |
38 | ctx = duk_create_heap_default();
39 | return ctx;
40 | }
41 |
42 |
43 | //
44 | // Destroy a previously allocated Duk heap
45 | //
46 |
47 | static void destroyDukHeap(void *ctx) {
48 | duk_destroy_heap(ctx);
49 | }
50 |
51 |
52 | //
53 | // Loads an SQLite value into the Duk stack.
54 | // Tries to match the types
55 | //
56 |
57 | static void pushSqliteToDuk(int i, sqlite3_value *value, duk_context *destination, duk_idx_t arr_idx) {
58 | duk_int_t n;
59 | duk_double_t d;
60 | const char *str;
61 |
62 | switch (sqlite3_value_type(value)) {
63 |
64 | case SQLITE_FLOAT:
65 | d = sqlite3_value_double(value);
66 | duk_push_number(destination, d);
67 | break;
68 |
69 | case SQLITE_INTEGER:
70 | n = sqlite3_value_int(value);
71 | duk_push_int(destination, n);
72 | break;
73 |
74 | case SQLITE_NULL:
75 | duk_push_null(destination);
76 | break;
77 |
78 | case SQLITE_TEXT:
79 | str = (char *) sqlite3_value_text(value);
80 | duk_push_string(destination, str);
81 | break;
82 |
83 | case SQLITE_BLOB:
84 | str = sqlite3_value_blob(value);
85 | n = sqlite3_value_bytes(value);
86 | duk_push_lstring(destination, str, n);
87 | break;
88 | }
89 |
90 | duk_put_prop_index(destination, arr_idx, i);
91 | }
92 |
93 |
94 | //
95 | // Gets a value from the Duk stack and returns it as an SQLite value.
96 | // Tries to match the types
97 | //
98 |
99 | static void popDukToSqlite(duk_context *from, sqlite3_context *ctx) {
100 | int n;
101 | double d;
102 | const char *str;
103 |
104 | if (duk_get_top_index(from) == DUK_INVALID_INDEX) {
105 | sqlite3_result_null(ctx);
106 | return;
107 | }
108 |
109 | switch (duk_get_type(from, -1)) {
110 |
111 | case DUK_TYPE_NONE:
112 | case DUK_TYPE_UNDEFINED:
113 | case DUK_TYPE_NULL:
114 | sqlite3_result_null(ctx);
115 | return;
116 |
117 | case DUK_TYPE_NUMBER:
118 | d = duk_get_number(from, -1);
119 | if (floor(d) == d) {
120 | sqlite3_result_int(ctx, (int) d);
121 | } else {
122 | sqlite3_result_double(ctx, d);
123 | }
124 | break;
125 |
126 | case DUK_TYPE_STRING:
127 | str = duk_get_string(from, -1);
128 | sqlite3_result_text(ctx, str, -1, SQLITE_TRANSIENT);
129 | break;
130 |
131 | case DUK_TYPE_BOOLEAN:
132 | n = duk_get_boolean(from, -1);
133 | sqlite3_result_int(ctx, n);
134 | break;
135 |
136 | case DUK_TYPE_BUFFER:
137 | str = duk_get_buffer(from, -1, (duk_size_t *)&n);
138 | sqlite3_result_blob(ctx, str, n, SQLITE_TRANSIENT);
139 | break;
140 |
141 | default:
142 | sqlite3_result_error(ctx, "Unsupported return type", -1);
143 | break;
144 | }
145 |
146 | duk_pop(from);
147 | }
148 |
149 |
150 | static duk_context *getAssociatedDukHeap(sqlite3_context *ctx) {
151 | return sqlite3_user_data(ctx);
152 | }
153 |
154 |
155 | //
156 | // Loads a code chunk into the Duk stack
157 | //
158 |
159 | static int pushDukChunk(duk_context *where, const unsigned char *code, const unsigned char *init,
160 | const unsigned char *final) {
161 |
162 | duk_pop_n(where, duk_get_top(where)); // clean the stack
163 |
164 | if (duk_pcompile_string(where, 0, (char *) code)) return 1;
165 |
166 | if (init != NULL && final != NULL) {
167 | if(duk_pcompile_string(where, 0, (char *) init)) return 2;
168 | if(duk_pcompile_string(where, 0, (char *) final)) return 3;
169 | }
170 |
171 | return 0;
172 | }
173 |
174 |
175 | //
176 | // Loads the passed values inside arg[]
177 | //
178 |
179 | static void pushDukParams(duk_context *where, int num_values, sqlite3_value **values) {
180 | int i;
181 | duk_idx_t arr_idx;
182 |
183 | arr_idx = duk_push_array(where);
184 |
185 | for (i = 0; i < num_values; i++) {
186 | pushSqliteToDuk(i, values[i], where, arr_idx);
187 | }
188 |
189 | duk_put_global_string(where, "arg");
190 | }
191 |
192 |
193 | //
194 | // Executes code chunk previously stored in the Duk stack
195 | //
196 |
197 | static void executeDukChunk(duk_context *where, int chunk) {
198 | duk_dup(where, chunk);
199 | duk_call(where, 0);
200 | }
201 |
202 |
203 | //
204 | // Check for return value
205 | //
206 | static int checkStackLen(duk_context *stack, sqlite3_context *ctx, int validLen) {
207 | if (duk_get_top(stack) != validLen) {
208 | sqlite3_result_error(ctx, "Invalid javascript stack length! " \
209 | "This normally happens if your code doesn't return any value.", -1);
210 | return 0;
211 | }
212 |
213 | return 1;
214 | }
215 |
216 | //
217 | // Store a pointer into a Duk table located at pos
218 | //
219 |
220 | static void storePointer(duk_context *where, int pos, const char *name, void *p) {
221 | duk_push_pointer(where, p);
222 | duk_put_prop_string(where, pos, name);
223 | }
224 |
225 |
226 | //
227 | // Find a pointer inside a Duk table located at pos
228 | //
229 |
230 | static void *findPointer(duk_context *where, int pos, const char *name) {
231 | void *result = NULL;
232 |
233 | if (duk_get_prop_string(where, pos, name)) {
234 | result = duk_to_pointer(where, -1);
235 | }
236 |
237 | duk_pop(where);
238 | return result;
239 | }
240 |
241 |
242 | //
243 | // Executes scalar function (called by SQLite)
244 | //
245 |
246 | static void sql_scalar_duk(sqlite3_context *ctx, int num_values, sqlite3_value **values) {
247 | duk_context *L;
248 |
249 | L = getAssociatedDukHeap(ctx);
250 | pushDukParams(L, num_values, values);
251 | executeDukChunk(L, 0);
252 | if (checkStackLen(L, ctx, 2)) popDukToSqlite(L, ctx); // stack size = 2 (code and return val)
253 | }
254 |
255 |
256 | //
257 | // Executes init and step parts of an aggregate function (called by SQLite)
258 | //
259 |
260 | static void sql_aggregate_duk(sqlite3_context *ctx, int num_values, sqlite3_value **values) {
261 | duk_context *L;
262 | int *notFirstTime = sqlite3_aggregate_context(ctx, sizeof(int));
263 |
264 | if (notFirstTime == NULL) {
265 | sqlite3_result_error_nomem(ctx);
266 | return;
267 | }
268 |
269 | L = getAssociatedDukHeap(ctx);
270 |
271 | if (*notFirstTime == 0) {
272 | // it must execute the init chunk once
273 | executeDukChunk(L, 1);
274 | duk_pop(L); // pop any return value
275 | *notFirstTime = 1;
276 | }
277 |
278 | pushDukParams(L, num_values, values);
279 | executeDukChunk(L, 0);
280 | duk_pop(L); // pop any return value
281 | }
282 |
283 |
284 | //
285 | // Executes the final code chunk of an aggregate function (called by SQLite)
286 | //
287 |
288 | static void sql_aggregate_duk_final(sqlite3_context *ctx) {
289 | duk_context *L;
290 | L = getAssociatedDukHeap(ctx);
291 |
292 | executeDukChunk(L, 2);
293 |
294 | if (checkStackLen(L, ctx, 4)) popDukToSqlite(L, ctx); // stack size = 4 (3x code and return val)
295 | }
296 |
297 |
298 | //
299 | // Check that all the function creation parameters are strings
300 | //
301 |
302 | static const char *checkCreateDukParameters(int num_values, sqlite3_value **values) {
303 | int i, adj;
304 |
305 | const char *errors[] = {
306 | "Invalid function name, string expected",
307 | "Invalid function code, string expected",
308 | "Invalid init code, string expected",
309 | "Invalid step code, string expected",
310 | "Invalid final code, string expected"
311 | };
312 |
313 | for (i = 0; i < num_values; i++) {
314 | adj = (num_values == 2 || i == 0 ? 0 : 1);
315 | if (sqlite3_value_type(values[i]) != SQLITE_TEXT) return errors[i + adj];
316 | }
317 |
318 | return NULL;
319 |
320 | }
321 |
322 |
323 | //
324 | // check result of code compilation and print a human readable result
325 | //
326 |
327 | static void messageCodeCompilingResult(sqlite3_context *ctx, int res, int num_values) {
328 | switch (res) {
329 | case 0:
330 | sqlite3_result_text(ctx, "ok", -1, SQLITE_TRANSIENT);
331 | break;
332 |
333 | case 1:
334 | if (num_values == 2) {
335 | sqlite3_result_error(ctx, "compilation problem, please check source code", -1);
336 | } else {
337 | sqlite3_result_error(ctx, "compilation problem, please check step source code", -1);
338 | }
339 | break;
340 |
341 | case 2:
342 | sqlite3_result_error(ctx, "compilation problem, please check init source code", -1);
343 | break;
344 |
345 | case 3:
346 | sqlite3_result_error(ctx, "compilation problem, please check final source code", -1);
347 | break;
348 | }
349 | }
350 |
351 |
352 | //
353 | // Create a new SQL js function (called by SQLite)
354 | //
355 |
356 | static void sql_createJS(sqlite3_context *ctx, int num_values, sqlite3_value **values) {
357 | duk_context *L, *functionTable;
358 |
359 | const char *msg, *name;
360 | int retVal;
361 |
362 | msg = checkCreateDukParameters(num_values, values);
363 | if (msg != NULL) {
364 | sqlite3_result_error(ctx, msg, -1);
365 | return;
366 | }
367 |
368 | name = (char *) sqlite3_value_text(values[0]);
369 |
370 | functionTable = getAssociatedDukHeap(ctx);
371 |
372 | L = findPointer(functionTable, 0, name);
373 | if (L == NULL) {
374 | L = createDukHeap();
375 | storePointer(functionTable, 0, name, L);
376 | // now register the function within SQLite
377 | if (num_values == 2) {
378 | // scalar
379 | sqlite3_create_function_v2(sqlite3_context_db_handle(ctx), (char *) name, -1,
380 | SQLITE_UTF8, (void *) L, sql_scalar_duk, NULL, NULL, destroyDukHeap);
381 | } else {
382 | // aggregate
383 | sqlite3_create_function_v2(sqlite3_context_db_handle(ctx), (char *) name, -1,
384 | SQLITE_UTF8, (void *) L, NULL, sql_aggregate_duk, sql_aggregate_duk_final, destroyDukHeap);
385 | }
386 |
387 | }
388 |
389 | // load the code inside the stack
390 | if (num_values == 2) {
391 | // scalar
392 | retVal = pushDukChunk(L, sqlite3_value_text(values[1]), NULL, NULL);
393 |
394 | } else {
395 | // aggregate
396 | retVal = pushDukChunk(L, sqlite3_value_text(values[2]), sqlite3_value_text(values[1]),
397 | sqlite3_value_text(values[3]));
398 | }
399 |
400 | if (retVal != 0) {
401 | sqlite3_create_function_v2(sqlite3_context_db_handle(ctx), (char *) name, -1,
402 | SQLITE_UTF8, NULL, NULL, NULL, NULL, NULL);
403 | }
404 |
405 | messageCodeCompilingResult(ctx, retVal, num_values);
406 | }
407 |
408 |
409 | //
410 | // Load data from a file (return a text or a blob)
411 | //
412 |
413 | static void sql_loadFile(sqlite3_context *ctx, int num_values, sqlite3_value **values) {
414 | FILE *f;
415 | char *buffer = 0;
416 | int length;
417 |
418 | f = fopen((const char *)sqlite3_value_text(values[0]), "rb");
419 |
420 | if (!f) {
421 | sqlite3_result_error(ctx, "Unable to open the file", -1);
422 | return;
423 | }
424 |
425 | fseek(f, 0, SEEK_END);
426 | length = ftell(f);
427 | fseek(f, 0, SEEK_SET);
428 |
429 | buffer = sqlite3_malloc(length + 1);
430 |
431 | if (buffer) {
432 | fread(buffer, 1, length, f);
433 |
434 | if (num_values == 2 && sqlite3_value_text(values[1])[0] == 'b') {
435 | sqlite3_result_blob(ctx, buffer, length, sqlite3_free);
436 | } else {
437 | sqlite3_result_text(ctx, buffer, length, sqlite3_free);
438 | }
439 | } else {
440 | sqlite3_result_error(ctx, "unable to get free memory to hold the file contents", -1);
441 | }
442 |
443 | fclose (f);
444 | }
445 |
446 |
447 | //
448 | // plugin main
449 | //
450 | #ifdef _WIN32
451 | __declspec(dllexport)
452 | #endif
453 | int sqlite3_extension_init(sqlite3 *db, char **error, const sqlite3_api_routines *api) {
454 | duk_context *mainHeap; // going to hold the list of created functions
455 |
456 | SQLITE_EXTENSION_INIT2(api);
457 |
458 |
459 | mainHeap = createDukHeap();
460 | if (mainHeap == NULL) return 0;
461 | duk_push_array(mainHeap);
462 |
463 | sqlite3_create_function_v2(db, "createjs", 2, SQLITE_UTF8, mainHeap, sql_createJS,
464 | NULL, NULL, NULL);
465 | sqlite3_create_function_v2(db, "createjs", 4, SQLITE_UTF8, mainHeap, sql_createJS,
466 | NULL, NULL, destroyDukHeap);
467 |
468 | sqlite3_create_function_v2(db, "loadfile", 1, SQLITE_UTF8, NULL, sql_loadFile,
469 | NULL, NULL, NULL);
470 | sqlite3_create_function_v2(db, "loadfile", 2, SQLITE_UTF8, NULL, sql_loadFile,
471 | NULL, NULL, NULL);
472 |
473 |
474 | return SQLITE_OK;
475 | }
476 |
--------------------------------------------------------------------------------