├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── examples └── http-json-github-emojis.sql ├── extism_sqlite3.c └── plugins └── http.wasm /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.dylib 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Dylibso 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | lib: 2 | $(CC) -shared -fPIC -o libextism_sqlite3.so extism_sqlite3.c -lextism -L. 3 | 4 | clean: 5 | rm -f ./libextism_sqlite3.so -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # extism-sqlite 2 | 3 | A `sqlite3` extension for loading [Extism](https://github.com/extism/extism) plugins 4 | 5 | ## Building 6 | 7 | `libextism.so` (or `libextism.dylib`) should be installed then: 8 | 9 | ```sh 10 | $ make 11 | ``` 12 | 13 | ## Examples 14 | 15 | Try running an example: 16 | 17 | ```sh 18 | $ sqlite3 < examples/http-json-github-emojis.sql 19 | ``` 20 | 21 | ## Usage 22 | 23 | Load the extism_sqlite3 plugin: 24 | 25 | ```sh 26 | sqlite> .load ./libextism_sqlite3 27 | ``` 28 | 29 | Load an extism plugin: 30 | 31 | ```sh 32 | sqlite> SELECT extism_load("plugins/http.wasm"); 33 | 0 34 | ``` 35 | 36 | The return value of `extism_load` is the plugin handle that should be passed to `extism_call`: 37 | 38 | ```sh 39 | sqlite> SELECT extism_call(0, "http_get", "http://example.com"); 40 | ... 41 | ``` -------------------------------------------------------------------------------- /examples/http-json-github-emojis.sql: -------------------------------------------------------------------------------- 1 | -- This example shows how to integrate an Extism plugin used to make an HTTP request with SQLite3. 2 | -- It uses an Extism WebAssembly plugin to fetch a JSON object then converts that JSON object into 3 | -- an SQLite table. 4 | 5 | -- Load extism_sqlite extension 6 | SELECT load_extension('./libextism_sqlite3'); 7 | 8 | -- Create temporary table for storing intermediate values 9 | CREATE TEMP TABLE IF NOT EXISTS tmp (key TEXT PRIMARY KEY, value TEXT); 10 | 11 | -- Load plugin from disk 12 | INSERT INTO tmp VALUES ('plugin', (SELECT extism_load('./plugins/http.wasm'))); 13 | 14 | -- URL to download JSON data from 15 | INSERT INTO tmp VALUES ('url', 'https://api.github.com/emojis'); 16 | 17 | -- Fetch JSON data 18 | INSERT INTO tmp VALUES ('data', (SELECT extism_call((SELECT value FROM tmp WHERE key = 'plugin'), 'http_get', (SELECT value from tmp WHERE key = 'url')))); 19 | 20 | -- Create table to store information from JSON dataset 21 | CREATE TABLE IF NOT EXISTS github_emojis (name TEXT PRIMARY KEY, url TEXT NOT NULL); 22 | 23 | -- Convert our JSON payload into a table 24 | INSERT INTO github_emojis (name, url) SELECT key, value FROM json_each((SELECT value FROM tmp where key = 'data')); 25 | 26 | -- Show emojis where the name contains the word 'smile' 27 | SELECT name from github_emojis WHERE name LIKE '%smile%'; 28 | -------------------------------------------------------------------------------- /extism_sqlite3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "extism.h" 6 | 7 | #include 8 | SQLITE_EXTENSION_INIT1; 9 | 10 | uint8_t *read_file(const char *filename, size_t *len) { 11 | 12 | FILE *fp = fopen(filename, "rb"); 13 | if (fp == NULL) { 14 | return NULL; 15 | } 16 | fseek(fp, 0, SEEK_END); 17 | size_t length = ftell(fp); 18 | fseek(fp, 0, SEEK_SET); 19 | 20 | uint8_t *data = malloc(length); 21 | if (data == NULL) { 22 | fclose(fp); 23 | return NULL; 24 | } 25 | 26 | fread(data, 1, length, fp); 27 | fclose(fp); 28 | 29 | *len = length; 30 | return data; 31 | } 32 | 33 | static void ext_load(sqlite3_context *ctx, int nargs, sqlite3_value **args) { 34 | if (nargs < 1) { 35 | sqlite3_result_error(ctx, "Expected 1 argument, got 0", -1); 36 | return; 37 | } 38 | 39 | ExtismContext *context = sqlite3_user_data(ctx); 40 | const char *file = (const char *)sqlite3_value_text(args[0]); 41 | size_t len = 0; 42 | uint8_t *wasm = read_file(file, &len); 43 | if (wasm == NULL) { 44 | sqlite3_result_error(ctx, "Plugin file not found", -1); 45 | return; 46 | } 47 | 48 | ExtismPlugin plugin = extism_plugin_new(context, wasm, len, NULL, 0, false); 49 | free(wasm); 50 | if (plugin < 0) { 51 | sqlite3_result_error(ctx, "Unable to load plugin", -1); 52 | return; 53 | } 54 | } 55 | 56 | static void ext_call(sqlite3_context *ctx, int nargs, sqlite3_value **args) { 57 | if (nargs < 3) { 58 | sqlite3_result_error(ctx, "Expected 3 arguments", -1); 59 | return; 60 | } 61 | 62 | ExtismContext *context = sqlite3_user_data(ctx); 63 | 64 | ExtismPlugin plugin = sqlite3_value_int(args[0]); 65 | const char *name = (const char *)sqlite3_value_text(args[1]); 66 | const uint8_t *input = sqlite3_value_text(args[2]); 67 | int32_t rc = extism_plugin_call(context, plugin, name, input, 68 | strlen((const char *)input)); 69 | if (rc != 0) { 70 | const char *err = extism_error(context, plugin); 71 | if (err != NULL) { 72 | sqlite3_result_error(ctx, err, -1); 73 | return; 74 | } 75 | 76 | sqlite3_result_error(ctx, "extism_call failed", -1); 77 | return; 78 | } 79 | 80 | ExtismSize out_len = extism_plugin_output_length(context, plugin); 81 | const uint8_t *out = extism_plugin_output_data(context, plugin); 82 | sqlite3_result_text(ctx, (const char *)out, out_len, SQLITE_TRANSIENT); 83 | } 84 | 85 | static void ext_free(sqlite3_context *ctx, int nargs, sqlite3_value **args) { 86 | if (nargs < 1) { 87 | sqlite3_result_error(ctx, "Expected 1 argument", -1); 88 | return; 89 | } 90 | 91 | ExtismContext *context = sqlite3_user_data(ctx); 92 | 93 | ExtismPlugin plugin = sqlite3_value_int(args[0]); 94 | extism_plugin_free(context, plugin); 95 | sqlite3_result_null(ctx); 96 | } 97 | 98 | int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, 99 | const sqlite3_api_routines *pApi) { 100 | int rc = SQLITE_OK; 101 | SQLITE_EXTENSION_INIT2(pApi); 102 | ExtismContext *context = extism_context_new(); 103 | sqlite3_create_function_v2(db, "extism_load", 1, SQLITE_UTF8, context, 104 | ext_load, NULL, NULL, (void *)extism_context_free); 105 | sqlite3_create_function_v2(db, "extism_call", 3, SQLITE_UTF8, context, 106 | ext_call, NULL, NULL, NULL); 107 | sqlite3_create_function_v2(db, "extism_free", 1, SQLITE_UTF8, context, 108 | ext_free, NULL, NULL, NULL); 109 | return rc; 110 | } 111 | -------------------------------------------------------------------------------- /plugins/http.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/extism/extism-sqlite3/becb3b27724d424e951b1564e62f1df70cc47697/plugins/http.wasm --------------------------------------------------------------------------------