From c32f4d2e1a0b1a2d054c5fd9d490d517fd53f8dc Mon Sep 17 00:00:00 2001 From: Oskari Timperi Date: Thu, 15 Sep 2022 21:00:10 +0300 Subject: Initial commit --- CMakeLists.txt | 40 + LICENSE.txt | 21 + src/beefysqlite_init.c | 21 + thirdparty/http.c | 1878 + thirdparty/sqlite-amalgamation/shell.c | 23795 +++ thirdparty/sqlite-amalgamation/sqlite3.c | 241619 +++++++++++++++++++++++++ thirdparty/sqlite-amalgamation/sqlite3.h | 12836 ++ thirdparty/sqlite-amalgamation/sqlite3ext.h | 701 + thirdparty/sqlite/ext/misc/compress.c | 131 + thirdparty/sqlite/ext/misc/fileio.c | 1030 + thirdparty/sqlite/ext/misc/percentile.c | 220 + thirdparty/sqlite/ext/misc/sha1.c | 393 + thirdparty/sqlite/ext/misc/uuid.c | 233 + thirdparty/vsv.c | 1962 + thirdparty/zlib/CMakeLists.txt | 249 + thirdparty/zlib/ChangeLog | 1578 + thirdparty/zlib/FAQ | 368 + thirdparty/zlib/INDEX | 68 + thirdparty/zlib/README | 118 + thirdparty/zlib/adler32.c | 186 + thirdparty/zlib/compress.c | 86 + thirdparty/zlib/crc32.c | 1116 + thirdparty/zlib/crc32.h | 9446 + thirdparty/zlib/deflate.c | 2211 + thirdparty/zlib/deflate.h | 346 + thirdparty/zlib/gzclose.c | 25 + thirdparty/zlib/gzguts.h | 219 + thirdparty/zlib/gzlib.c | 639 + thirdparty/zlib/gzread.c | 652 + thirdparty/zlib/gzwrite.c | 677 + thirdparty/zlib/infback.c | 641 + thirdparty/zlib/inffast.c | 323 + thirdparty/zlib/inffast.h | 11 + thirdparty/zlib/inffixed.h | 94 + thirdparty/zlib/inflate.c | 1592 + thirdparty/zlib/inflate.h | 126 + thirdparty/zlib/inftrees.c | 304 + thirdparty/zlib/inftrees.h | 62 + thirdparty/zlib/test/example.c | 601 + thirdparty/zlib/test/infcover.c | 671 + thirdparty/zlib/test/minigzip.c | 651 + thirdparty/zlib/treebuild.xml | 116 + thirdparty/zlib/trees.c | 1182 + thirdparty/zlib/trees.h | 128 + thirdparty/zlib/uncompr.c | 93 + thirdparty/zlib/win32/DLL_FAQ.txt | 397 + thirdparty/zlib/win32/Makefile.bor | 109 + thirdparty/zlib/win32/Makefile.gcc | 177 + thirdparty/zlib/win32/Makefile.msc | 159 + thirdparty/zlib/win32/README-WIN32.txt | 103 + thirdparty/zlib/win32/VisualC.txt | 3 + thirdparty/zlib/win32/zlib.def | 97 + thirdparty/zlib/win32/zlib1.rc | 40 + thirdparty/zlib/zconf.h.cmakein | 536 + thirdparty/zlib/zconf.h.in | 534 + thirdparty/zlib/zconf.h.included | 534 + thirdparty/zlib/zlib.h | 1935 + thirdparty/zlib/zlib.map | 100 + thirdparty/zlib/zlib.pc.cmakein | 13 + thirdparty/zlib/zlib.pc.in | 13 + thirdparty/zlib/zlib2ansi | 152 + thirdparty/zlib/zutil.c | 325 + thirdparty/zlib/zutil.h | 274 + 63 files changed, 314960 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE.txt create mode 100644 src/beefysqlite_init.c create mode 100644 thirdparty/http.c create mode 100644 thirdparty/sqlite-amalgamation/shell.c create mode 100644 thirdparty/sqlite-amalgamation/sqlite3.c create mode 100644 thirdparty/sqlite-amalgamation/sqlite3.h create mode 100644 thirdparty/sqlite-amalgamation/sqlite3ext.h create mode 100644 thirdparty/sqlite/ext/misc/compress.c create mode 100644 thirdparty/sqlite/ext/misc/fileio.c create mode 100644 thirdparty/sqlite/ext/misc/percentile.c create mode 100644 thirdparty/sqlite/ext/misc/sha1.c create mode 100644 thirdparty/sqlite/ext/misc/uuid.c create mode 100644 thirdparty/vsv.c create mode 100644 thirdparty/zlib/CMakeLists.txt create mode 100644 thirdparty/zlib/ChangeLog create mode 100644 thirdparty/zlib/FAQ create mode 100644 thirdparty/zlib/INDEX create mode 100644 thirdparty/zlib/README create mode 100644 thirdparty/zlib/adler32.c create mode 100644 thirdparty/zlib/compress.c create mode 100644 thirdparty/zlib/crc32.c create mode 100644 thirdparty/zlib/crc32.h create mode 100644 thirdparty/zlib/deflate.c create mode 100644 thirdparty/zlib/deflate.h create mode 100644 thirdparty/zlib/gzclose.c create mode 100644 thirdparty/zlib/gzguts.h create mode 100644 thirdparty/zlib/gzlib.c create mode 100644 thirdparty/zlib/gzread.c create mode 100644 thirdparty/zlib/gzwrite.c create mode 100644 thirdparty/zlib/infback.c create mode 100644 thirdparty/zlib/inffast.c create mode 100644 thirdparty/zlib/inffast.h create mode 100644 thirdparty/zlib/inffixed.h create mode 100644 thirdparty/zlib/inflate.c create mode 100644 thirdparty/zlib/inflate.h create mode 100644 thirdparty/zlib/inftrees.c create mode 100644 thirdparty/zlib/inftrees.h create mode 100644 thirdparty/zlib/test/example.c create mode 100644 thirdparty/zlib/test/infcover.c create mode 100644 thirdparty/zlib/test/minigzip.c create mode 100644 thirdparty/zlib/treebuild.xml create mode 100644 thirdparty/zlib/trees.c create mode 100644 thirdparty/zlib/trees.h create mode 100644 thirdparty/zlib/uncompr.c create mode 100644 thirdparty/zlib/win32/DLL_FAQ.txt create mode 100644 thirdparty/zlib/win32/Makefile.bor create mode 100644 thirdparty/zlib/win32/Makefile.gcc create mode 100644 thirdparty/zlib/win32/Makefile.msc create mode 100644 thirdparty/zlib/win32/README-WIN32.txt create mode 100644 thirdparty/zlib/win32/VisualC.txt create mode 100644 thirdparty/zlib/win32/zlib.def create mode 100644 thirdparty/zlib/win32/zlib1.rc create mode 100644 thirdparty/zlib/zconf.h.cmakein create mode 100644 thirdparty/zlib/zconf.h.in create mode 100644 thirdparty/zlib/zconf.h.included create mode 100644 thirdparty/zlib/zlib.h create mode 100644 thirdparty/zlib/zlib.map create mode 100644 thirdparty/zlib/zlib.pc.cmakein create mode 100644 thirdparty/zlib/zlib.pc.in create mode 100644 thirdparty/zlib/zlib2ansi create mode 100644 thirdparty/zlib/zutil.c create mode 100644 thirdparty/zlib/zutil.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2e47d21 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) + +project(beefysqlite VERSION 0.1.0 LANGUAGES C) + +add_executable(beefysqlite + "src/beefysqlite_init.c" + "thirdparty/sqlite-amalgamation/shell.c" + "thirdparty/sqlite-amalgamation/sqlite3.c" + "thirdparty/sqlite/ext/misc/compress.c" + "thirdparty/sqlite/ext/misc/percentile.c" + "thirdparty/sqlite/ext/misc/sha1.c" + "thirdparty/sqlite/ext/misc/uuid.c" + "thirdparty/vsv.c" + "thirdparty/http.c" +) + +add_subdirectory(thirdparty/zlib) + +target_include_directories(beefysqlite PRIVATE + thirdparty/sqlite-amalgamation + thirdparty/zlib + ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/zlib +) + +target_compile_definitions(beefysqlite PRIVATE + SQLITE_CORE + SQLITE_SHELL_INIT_PROC=beefysqlite_init + SQLITE_HAVE_ZLIB + SQLITE_ENABLE_FTS5 + SQLITE_ENABLE_MATH_FUNCTIONS +) + +target_link_libraries(beefysqlite PRIVATE zlibstatic) + +if(WIN32) + target_compile_definitions(beefysqlite PRIVATE HTTP_BACKEND_WINHTTP) + target_link_libraries(beefysqlite PRIVATE winhttp) +else() + target_compile_definitions(beefysqlite PRIVATE HTTP_BACKEND_CURL) +endif() diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..9ba0b79 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Oskari Timperi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/beefysqlite_init.c b/src/beefysqlite_init.c new file mode 100644 index 0000000..454c7ff --- /dev/null +++ b/src/beefysqlite_init.c @@ -0,0 +1,21 @@ +#include +#include + +typedef void (*entrypoint)(void); + +int sqlite3_compress_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi); +int sqlite3_http_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi); +int sqlite3_percentile_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi); +int sqlite3_sha_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi); +int sqlite3_uuid_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi); +int sqlite3_vsv_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi); + +void beefysqlite_init() { + sqlite3_initialize(); + sqlite3_auto_extension((entrypoint)sqlite3_compress_init); + sqlite3_auto_extension((entrypoint)sqlite3_http_init); + sqlite3_auto_extension((entrypoint)sqlite3_percentile_init); + sqlite3_auto_extension((entrypoint)sqlite3_sha_init); + sqlite3_auto_extension((entrypoint)sqlite3_uuid_init); + sqlite3_auto_extension((entrypoint)sqlite3_vsv_init); +} diff --git a/thirdparty/http.c b/thirdparty/http.c new file mode 100644 index 0000000..7b7b6c8 --- /dev/null +++ b/thirdparty/http.c @@ -0,0 +1,1878 @@ + +/********** src/http.h **********/ + +/** + * HTTP client extension for SQLite. + * + * https://github.com/oskaritimperi/sqlite-http-c + * + * MIT License + * + * Copyright (c) 2022 Oskari Timperi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef HTTP_H +#define HTTP_H + +#include "sqlite3ext.h" + +typedef struct http_request http_request; +struct http_request { + char* zMethod; + char* zUrl; + const void* pBody; + sqlite3_int64 szBody; + const char* zHeaders; +}; + +typedef struct http_response http_response; +struct http_response { + void* pBody; + sqlite3_int64 szBody; + char* zHeaders; + int szHeaders; + int iStatusCode; + char* zStatus; +}; + +int http_do_request(http_request* req, http_response* resp, char** ppErrMsg); + +int http_next_header(const char* headers, + int size, + int* pParsed, + const char** ppName, + int* pNameSize, + const char** ppValue, + int* pValueSize); + +void remove_all_but_last_headers(char* zHeaders); +void separate_status_and_headers(char** ppStatus, char* zHeaders); + +#endif + +/********** src/http.c **********/ + +#include "http.h" + +SQLITE_EXTENSION_INIT1 + +#include +#include +#include + +typedef struct http_vtab http_vtab; +struct http_vtab { + sqlite3_vtab base; + char* zMethod; +}; + +typedef struct http_cursor http_cursor; +struct http_cursor { + sqlite3_vtab_cursor base; + sqlite3_int64 iRowid; + http_request req; + http_response resp; +}; + +// If zHeaders contains headers for multiple responses, then this will strip +// headers from all but the last response. +void remove_all_but_last_headers(char* zHeaders) { + int szHeaders = strlen(zHeaders); + char* pLastTerminator = NULL; + char* pSearch = zHeaders; + + for (;;) { + char* p = strstr(pSearch, "\r\n\r\n"); + // If there isn't more terminators, we are done here + if (!p) { + break; + } + // If it's at the end of zHeaders, we are done here + if (szHeaders - (p - zHeaders) == 4) { + break; + } + pSearch = p + 4; + pLastTerminator = p; + } + + if (pLastTerminator) { + pLastTerminator += 4; + memmove(zHeaders, pLastTerminator, strlen(pLastTerminator) + 1); + } +} + +// Separate the status and header lines +void separate_status_and_headers(char** ppStatus, char* zHeaders) { + char* cr = strchr(zHeaders, '\r'); + *ppStatus = sqlite3_mprintf("%.*s", cr - zHeaders, zHeaders); + memmove(zHeaders, cr + 2, strlen(cr + 2) + 1); +} + +static int httpConnect(sqlite3* db, + void* pAux, + int argc, + const char* const* argv, + sqlite3_vtab** ppVtab, + char** pzErr) { + http_vtab* pNew; + int rc; + + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(response_status TEXT, " + "response_status_code INT, response_headers TEXT, " + "response_body BLOB, request_method TEXT HIDDEN, " + "request_url TEXT HIDDEN, " + "request_headers TEXT HIDDEN, " + "request_body BLOB HIDDEN)"); + +#define HTTP_COL_RESPONSE_STATUS 0 +#define HTTP_COL_RESPONSE_STATUS_CODE 1 +#define HTTP_COL_RESPONSE_HEADERS 2 +#define HTTP_COL_RESPONSE_BODY 3 +#define HTTP_COL_REQUEST_METHOD 4 +#define HTTP_COL_REQUEST_URL 5 +#define HTTP_COL_REQUEST_HEADERS 6 +#define HTTP_COL_REQUEST_BODY 7 + + if (rc == SQLITE_OK) { + pNew = sqlite3_malloc(sizeof(*pNew)); + *ppVtab = (sqlite3_vtab*)pNew; + if (pNew == 0) + return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + if (sqlite3_stricmp(argv[0], "http_get") == 0) { + pNew->zMethod = sqlite3_mprintf("GET"); + } else if (sqlite3_stricmp(argv[0], "http_post") == 0) { + pNew->zMethod = sqlite3_mprintf("POST"); + } + } + return rc; +} + +static int httpDisconnect(sqlite3_vtab* pVtab) { + http_vtab* p = (http_vtab*)pVtab; + sqlite3_free(p->zMethod); + sqlite3_free(p); + return SQLITE_OK; +} + +static int httpOpen(sqlite3_vtab* p, sqlite3_vtab_cursor** ppCursor) { + http_cursor* pCur; + http_vtab* pVtab = (http_vtab*)p; + pCur = sqlite3_malloc(sizeof(*pCur)); + if (pCur == 0) + return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + if (pVtab->zMethod) { + pCur->req.zMethod = sqlite3_mprintf("%s", pVtab->zMethod); + } + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static int httpClose(sqlite3_vtab_cursor* cur) { + http_cursor* pCur = (http_cursor*)cur; + sqlite3_free(pCur->resp.pBody); + sqlite3_free(pCur->req.zUrl); + sqlite3_free(pCur->req.zMethod); + sqlite3_free(pCur); + return SQLITE_OK; +} + +static int httpNext(sqlite3_vtab_cursor* cur) { + http_cursor* pCur = (http_cursor*)cur; + pCur->iRowid++; + return SQLITE_OK; +} + +static int httpColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int i) { + http_cursor* pCur = (http_cursor*)cur; + + switch (i) { + case HTTP_COL_RESPONSE_STATUS: + sqlite3_result_text(ctx, pCur->resp.zStatus, -1, SQLITE_TRANSIENT); + break; + + case HTTP_COL_RESPONSE_STATUS_CODE: + sqlite3_result_int(ctx, pCur->resp.iStatusCode); + break; + + case HTTP_COL_RESPONSE_HEADERS: + sqlite3_result_text(ctx, pCur->resp.zHeaders, -1, SQLITE_TRANSIENT); + break; + + case HTTP_COL_RESPONSE_BODY: + sqlite3_result_blob(ctx, pCur->resp.pBody, pCur->resp.szBody, SQLITE_TRANSIENT); + break; + + case HTTP_COL_REQUEST_METHOD: + sqlite3_result_text(ctx, pCur->req.zMethod, -1, SQLITE_TRANSIENT); + break; + + case HTTP_COL_REQUEST_URL: + sqlite3_result_text(ctx, pCur->req.zUrl, -1, SQLITE_TRANSIENT); + break; + + case HTTP_COL_REQUEST_HEADERS: + if (pCur->req.zHeaders) { + sqlite3_result_text(ctx, pCur->req.zHeaders, -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_null(ctx); + } + break; + + case HTTP_COL_REQUEST_BODY: + if (pCur->req.pBody) { + sqlite3_result_blob(ctx, pCur->req.pBody, pCur->req.szBody, SQLITE_TRANSIENT); + } else { + sqlite3_result_null(ctx); + } + break; + } + + return SQLITE_OK; +} + +static int httpRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) { + http_cursor* pCur = (http_cursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +static int httpEof(sqlite3_vtab_cursor* cur) { + http_cursor* pCur = (http_cursor*)cur; + return pCur->iRowid >= 1; +} + +#define HTTP_FLAG_METHOD 1 +#define HTTP_FLAG_URL 2 +#define HTTP_FLAG_HEADERS 4 +#define HTTP_FLAG_BODY 8 + +static int httpFilter(sqlite3_vtab_cursor* pVtabCursor, + int idxNum, + const char* idxStr, + int argc, + sqlite3_value** argv) { + http_cursor* pCur = (http_cursor*)pVtabCursor; + http_vtab* pVtab = (http_vtab*)pVtabCursor->pVtab; + int bIsDo = pVtab->zMethod == NULL; + char* zErrMsg = NULL; + int rc; + if (bIsDo) { + pCur->req.zMethod = sqlite3_mprintf("%s", sqlite3_value_text(argv[0])); + pCur->req.zUrl = sqlite3_mprintf("%s", sqlite3_value_text(argv[1])); + if (idxNum & HTTP_FLAG_HEADERS) { + pCur->req.zHeaders = sqlite3_mprintf("%s", sqlite3_value_text(argv[2])); + } + if (idxNum & HTTP_FLAG_BODY) { + pCur->req.szBody = sqlite3_value_bytes(argv[3]); + pCur->req.pBody = sqlite3_value_blob(argv[3]); + } + } else { + pCur->req.zUrl = sqlite3_mprintf("%s", sqlite3_value_text(argv[0])); + if (idxNum & HTTP_FLAG_HEADERS) { + pCur->req.zHeaders = sqlite3_mprintf("%s", sqlite3_value_text(argv[1])); + } + if (idxNum & HTTP_FLAG_BODY) { + pCur->req.szBody = sqlite3_value_bytes(argv[2]); + pCur->req.pBody = sqlite3_value_blob(argv[2]); + } + } + rc = http_do_request(&pCur->req, &pCur->resp, &zErrMsg); + if (rc != SQLITE_OK) { + sqlite3_free(pCur->base.pVtab->zErrMsg); + pCur->base.pVtab->zErrMsg = zErrMsg; + } + return rc; +} + +static int httpBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) { + http_vtab* pTab = (http_vtab*)tab; + int bIsDo = pTab->zMethod == NULL; + int i; + int idxNum = 0; + const struct sqlite3_index_constraint* pConstraint; + + pConstraint = pIdxInfo->aConstraint; + + for (i = 0; i < pIdxInfo->nConstraint; ++i, ++pConstraint) { + if (!pIdxInfo->aConstraint[i].usable) { + return SQLITE_CONSTRAINT; + } + if (pConstraint->op != SQLITE_INDEX_CONSTRAINT_EQ) { + return SQLITE_CONSTRAINT; + }; + switch (pConstraint->iColumn) { + case HTTP_COL_REQUEST_METHOD: + if (bIsDo) { + idxNum |= HTTP_FLAG_METHOD; + } else { + idxNum |= HTTP_FLAG_URL; + } + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + break; + + case HTTP_COL_REQUEST_URL: + if (bIsDo) { + idxNum |= HTTP_FLAG_URL; + } else { + idxNum |= HTTP_FLAG_HEADERS; + } + pIdxInfo->aConstraintUsage[i].argvIndex = 2; + pIdxInfo->aConstraintUsage[i].omit = 1; + break; + + case HTTP_COL_REQUEST_HEADERS: + if (bIsDo) { + idxNum |= HTTP_FLAG_HEADERS; + } else { + idxNum |= HTTP_FLAG_BODY; + } + pIdxInfo->aConstraintUsage[i].argvIndex = 3; + pIdxInfo->aConstraintUsage[i].omit = 1; + break; + + case HTTP_COL_REQUEST_BODY: + if (bIsDo) { + idxNum |= HTTP_FLAG_BODY; + } else { + sqlite3_free(tab->zErrMsg); + tab->zErrMsg = sqlite3_mprintf("too many arguments"); + return SQLITE_ERROR; + } + pIdxInfo->aConstraintUsage[i].argvIndex = 4; + pIdxInfo->aConstraintUsage[i].omit = 1; + break; + } + } + + if (bIsDo && !(idxNum & HTTP_FLAG_METHOD)) { + sqlite3_free(tab->zErrMsg); + tab->zErrMsg = sqlite3_mprintf("method missing"); + return SQLITE_ERROR; + } + + if (!(idxNum & HTTP_FLAG_URL)) { + sqlite3_free(tab->zErrMsg); + tab->zErrMsg = sqlite3_mprintf("url missing"); + return SQLITE_ERROR; + } + + pIdxInfo->estimatedCost = (double)1; + pIdxInfo->estimatedRows = 1; + pIdxInfo->idxNum = idxNum; + + return SQLITE_OK; +} + +static sqlite3_module httpModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ httpConnect, + /* xBestIndex */ httpBestIndex, + /* xDisconnect */ httpDisconnect, + /* xDestroy */ 0, + /* xOpen */ httpOpen, + /* xClose */ httpClose, + /* xFilter */ httpFilter, + /* xNext */ httpNext, + /* xEof */ httpEof, + /* xColumn */ httpColumn, + /* xRowid */ httpRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0, +}; + +static void httpSimpleFunc(sqlite3_context* ctx, + int argc, + sqlite3_value** argv, + const char* zTable, + const char* zColumn, + int bText) { + char* zSql; + int rc; + sqlite3_stmt* pStmt; + + if (argc == 0) { + sqlite3_result_error(ctx, "not enough arguments", -1); + return; + } + + if (argc == 1) { + zSql = sqlite3_mprintf( + "select \"%w\" from \"%w\"(%Q)", zColumn, zTable, sqlite3_value_text(argv[0])); + } + + if (argc == 2) { + zSql = sqlite3_mprintf("select \"%w\" from \"%w\"(%Q, %Q)", + zColumn, + zTable, + sqlite3_value_text(argv[0]), + sqlite3_value_text(argv[1])); + } + + if (argc == 3) { + zSql = sqlite3_mprintf("select \"%w\" from \"%w\"(%Q, %Q, %Q)", + zColumn, + zTable, + sqlite3_value_text(argv[0]), + sqlite3_value_text(argv[1]), + sqlite3_value_text(argv[2])); + } + + if (argc >= 4) { + zSql = sqlite3_mprintf("select \"%w\" from \"%w\"(%Q, %Q, %Q, %Q)", + zColumn, + zTable, + sqlite3_value_text(argv[0]), + sqlite3_value_text(argv[1]), + sqlite3_value_text(argv[2]), + sqlite3_value_text(argv[2])); + } + + if (zSql == 0) { + sqlite3_result_error_code(ctx, SQLITE_NOMEM); + return; + } + + rc = sqlite3_prepare_v2(sqlite3_context_db_handle(ctx), zSql, -1, &pStmt, 0); + + sqlite3_free(zSql); + + if (rc != SQLITE_OK) { + sqlite3_result_error_code(ctx, rc); + return; + } + + rc = sqlite3_step(pStmt); + + if (rc == SQLITE_ROW) { + int size = sqlite3_column_bytes(pStmt, 0); + if (bText) { + sqlite3_result_text(ctx, sqlite3_column_text(pStmt, 0), size, SQLITE_TRANSIENT); + } else { + sqlite3_result_blob(ctx, sqlite3_column_blob(pStmt, 0), size, SQLITE_TRANSIENT); + } + sqlite3_finalize(pStmt); + return; + } + + rc = sqlite3_finalize(pStmt); + if (rc != SQLITE_OK) { + sqlite3_result_error_code(ctx, rc); + } +} + +static void httpGetBodyFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + httpSimpleFunc(ctx, argc, argv, "http_get", "response_body", 0); +} + +static void httpPostBodyFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + httpSimpleFunc(ctx, argc, argv, "http_post", "response_body", 0); +} + +static void httpDoBodyFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + httpSimpleFunc(ctx, argc, argv, "http_do", "response_body", 0); +} + +static void httpGetHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + httpSimpleFunc(ctx, argc, argv, "http_get", "response_headers", 1); +} + +static void httpPostHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + httpSimpleFunc(ctx, argc, argv, "http_post", "response_headers", 1); +} + +static void httpDoHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + httpSimpleFunc(ctx, argc, argv, "http_do", "response_headers", 1); +} + +static void httpHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + char* zHeaders = NULL; + int i; + int size = 0; + int offset = 0; + if (argc < 2) { + sqlite3_result_error(ctx, "http_headers: expected at least 2 arguments", -1); + return; + } + if (argc % 2 != 0) { + sqlite3_result_error(ctx, "http_headers: even number of arguments expected", -1); + return; + } + for (i = 0; i < argc; i += 2) { + size += sqlite3_value_bytes(argv[i]) + 2; + size += sqlite3_value_bytes(argv[i + 1]) + 2; + } + zHeaders = sqlite3_malloc(size); + if (!zHeaders) { + sqlite3_result_error_code(ctx, SQLITE_NOMEM); + return; + } + for (i = 0; i < argc; i += 2) { + memcpy(zHeaders + offset, sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + offset += sqlite3_value_bytes(argv[i]); + + memcpy(zHeaders + offset, ": ", 2); + offset += 2; + + memcpy( + zHeaders + offset, sqlite3_value_text(argv[i + 1]), sqlite3_value_bytes(argv[i + 1])); + offset += sqlite3_value_bytes(argv[i + 1]); + + memcpy(zHeaders + offset, "\r\n", 2); + offset += 2; + } + assert(offset == size); + sqlite3_result_text(ctx, zHeaders, size, sqlite3_free); +} + +static void httpHeadersHasFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + int zHeadersSize = sqlite3_value_bytes(argv[0]); + const char* zHeaders = sqlite3_value_text(argv[0]); + int zHeaderSize = sqlite3_value_bytes(argv[1]); + const char* zHeader = sqlite3_value_text(argv[1]); + while (zHeadersSize > 0) { + int iParsed = 0; + const char* pName; + int iNameSize; + int rc = http_next_header(zHeaders, zHeadersSize, &iParsed, &pName, &iNameSize, NULL, NULL); + if (rc == SQLITE_ERROR) { + sqlite3_result_error(ctx, "http_headers_has: malformed headers", -1); + return; + } + if (rc == SQLITE_DONE) { + sqlite3_result_int(ctx, 0); + return; + } + zHeaders += iParsed; + zHeadersSize -= iParsed; + if (iNameSize == zHeaderSize && sqlite3_strnicmp(zHeader, pName, zHeaderSize) == 0) { + sqlite3_result_int(ctx, 1); + return; + } + } + sqlite3_result_int(ctx, 0); +} + +static void httpHeadersGetFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + int zHeadersSize = sqlite3_value_bytes(argv[0]); + const char* zHeaders = sqlite3_value_text(argv[0]); + int zHeaderSize = sqlite3_value_bytes(argv[1]); + const char* zHeader = sqlite3_value_text(argv[1]); + while (zHeadersSize > 0) { + int iParsed = 0; + const char* pName; + int iNameSize; + const char* pValue; + int iValueSize; + int rc = http_next_header( + zHeaders, zHeadersSize, &iParsed, &pName, &iNameSize, &pValue, &iValueSize); + if (rc == SQLITE_ERROR) { + sqlite3_result_error(ctx, "http_headers_get: malformed headers", -1); + return; + } + if (rc == SQLITE_DONE) { + sqlite3_result_int(ctx, 0); + return; + } + zHeaders += iParsed; + zHeadersSize -= iParsed; + if (iNameSize == zHeaderSize && sqlite3_strnicmp(zHeader, pName, zHeaderSize) == 0) { + sqlite3_result_text(ctx, pValue, iValueSize, SQLITE_TRANSIENT); + return; + } + } +} + +#define HTTP_HEADERS_EACH_COL_NAME 0 +#define HTTP_HEADERS_EACH_COL_VALUE 1 +#define HTTP_HEADERS_EACH_COL_HEADERS 2 + +typedef struct http_headers_each_vtab http_headers_each_vtab; +struct http_headers_each_vtab { + sqlite3_vtab base; +}; + +typedef struct http_headers_each_cursor http_headers_each_cursor; +struct http_headers_each_cursor { + sqlite3_vtab_cursor base; + sqlite3_int64 iRowid; + const char* zHeaders; + int zHeadersSize; + const char* p; + const char* name; + int nameSize; + const char* value; + int valueSize; + int available; + int done; +}; + +static int httpHeadersEachConnect(sqlite3* db, + void* pAux, + int argc, + const char* const* argv, + sqlite3_vtab** ppVtab, + char** pzErr) { + http_headers_each_vtab* pNew; + int rc; + rc = sqlite3_declare_vtab(db, "CREATE TABLE x(name TEXT, value TEXT, headers TEXT HIDDEN)"); + if (rc == SQLITE_OK) { + pNew = sqlite3_malloc(sizeof(*pNew)); + *ppVtab = (sqlite3_vtab*)pNew; + if (pNew == 0) + return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + } + return rc; +} + +static int httpHeadersEachDisconnect(sqlite3_vtab* pVtab) { + http_headers_each_vtab* p = (http_headers_each_vtab*)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +static int httpHeadersEachOpen(sqlite3_vtab* p, sqlite3_vtab_cursor** ppCursor) { + http_headers_each_cursor* pCur; + pCur = sqlite3_malloc(sizeof(*pCur)); + if (pCur == 0) + return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static int httpHeadersEachClose(sqlite3_vtab_cursor* cur) { + http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur; + sqlite3_free(pCur); + return SQLITE_OK; +} + +static int httpHeadersEachNext(sqlite3_vtab_cursor* cur) { + http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur; + pCur->iRowid++; + int nparsed = 0; + int rc = http_next_header(pCur->p, + pCur->available, + &nparsed, + &pCur->name, + &pCur->nameSize, + &pCur->value, + &pCur->valueSize); + pCur->p += nparsed; + pCur->available -= nparsed; + if (rc == SQLITE_ERROR) { + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = sqlite3_mprintf("http_headers_each: malformed headers"); + return SQLITE_ERROR; + } + pCur->done = rc == SQLITE_DONE; + return SQLITE_OK; +} + +static int httpHeadersEachColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int i) { + http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur; + switch (i) { + case HTTP_HEADERS_EACH_COL_NAME: + sqlite3_result_text(ctx, pCur->name, pCur->nameSize, SQLITE_TRANSIENT); + break; + + case HTTP_HEADERS_EACH_COL_VALUE: + sqlite3_result_text(ctx, pCur->value, pCur->valueSize, SQLITE_TRANSIENT); + break; + + case HTTP_HEADERS_EACH_COL_HEADERS: + sqlite3_result_text(ctx, pCur->zHeaders, pCur->zHeadersSize, SQLITE_TRANSIENT); + break; + + default: + sqlite3_result_error(ctx, "unknown column", -1); + break; + } + return SQLITE_OK; +} + +static int httpHeadersEachRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) { + http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +static int httpHeadersEachEof(sqlite3_vtab_cursor* cur) { + http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur; + return pCur->done; +} + +static int httpHeadersEachFilter(sqlite3_vtab_cursor* pVtabCursor, + int idxNum, + const char* idxStr, + int argc, + sqlite3_value** argv) { + http_headers_each_cursor* pCur = (http_headers_each_cursor*)pVtabCursor; + pCur->zHeadersSize = sqlite3_value_bytes(argv[0]); + pCur->available = pCur->zHeadersSize; + pCur->zHeaders = sqlite3_value_text(argv[0]); + pCur->p = pCur->zHeaders; + int nparsed = 0; + int rc = http_next_header(pCur->p, + pCur->available, + &nparsed, + &pCur->name, + &pCur->nameSize, + &pCur->value, + &pCur->valueSize); + pCur->p += nparsed; + pCur->available -= nparsed; + pCur->iRowid = 1; + if (rc == SQLITE_ERROR) { + sqlite3_free(pVtabCursor->pVtab->zErrMsg); + pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf("http_headers_each: malformed headers"); + return SQLITE_ERROR; + } + pCur->done = rc == SQLITE_DONE; + return SQLITE_OK; +} + +static int httpHeadersEachBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) { + http_vtab* pTab = (http_vtab*)tab; + int bHeadersSeen = 0; + + for (int i = 0; i < pIdxInfo->nConstraint; ++i) { + if (pIdxInfo->aConstraint[i].iColumn == HTTP_HEADERS_EACH_COL_HEADERS) { + if (!pIdxInfo->aConstraint[i].usable) { + return SQLITE_CONSTRAINT; + } + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + bHeadersSeen = 1; + } + } + + if (!bHeadersSeen) { + sqlite3_free(tab->zErrMsg); + tab->zErrMsg = sqlite3_mprintf("headers missing"); + return SQLITE_ERROR; + } + + pIdxInfo->estimatedCost = (double)1; + // pIdxInfo->estimatedRows = 1; + + return SQLITE_OK; +} + +static sqlite3_module httpHeadersEachModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ httpHeadersEachConnect, + /* xBestIndex */ httpHeadersEachBestIndex, + /* xDisconnect */ httpHeadersEachDisconnect, + /* xDestroy */ 0, + /* xOpen */ httpHeadersEachOpen, + /* xClose */ httpHeadersEachClose, + /* xFilter */ httpHeadersEachFilter, + /* xNext */ httpHeadersEachNext, + /* xEof */ httpHeadersEachEof, + /* xColumn */ httpHeadersEachColumn, + /* xRowid */ httpHeadersEachRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0, +}; + +static const struct Func { + const char* name; + void (*xFunc)(sqlite3_context*, int, sqlite3_value**); +} funcs[] = { + {"http_get_body", httpGetBodyFunc}, + {"http_post_body", httpPostBodyFunc}, + {"http_do_body", httpDoBodyFunc}, + {"http_get_headers", httpGetHeadersFunc}, + {"http_post_headers", httpPostHeadersFunc}, + {"http_do_headers", httpDoHeadersFunc}, + {"http_headers", httpHeadersFunc}, + {"http_headers_has", httpHeadersHasFunc}, + {"http_headers_get", httpHeadersGetFunc}, + {NULL, NULL}, +}; + +static const struct Module { + const char* name; + const sqlite3_module* module; +} modules[] = { + {"http_get", &httpModule}, + {"http_post", &httpModule}, + {"http_do", &httpModule}, + {"http_headers_each", &httpHeadersEachModule}, + {NULL, NULL}, +}; + +#ifdef _WIN32 +__declspec(dllexport) +#endif + int sqlite3_http_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi) { + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); + int i; + for (i = 0; funcs[i].name && rc == SQLITE_OK; ++i) { + rc = sqlite3_create_function( + db, funcs[i].name, -1, SQLITE_UTF8, NULL, funcs[i].xFunc, NULL, NULL); + } + for (i = 0; modules[i].name && rc == SQLITE_OK; ++i) { + rc = sqlite3_create_module(db, modules[i].name, modules[i].module, 0); + } + return rc; +} + +/********** src/http_backend_curl.c **********/ + +#ifdef HTTP_BACKEND_CURL + +#include "http.h" + +#include +#include + +SQLITE_EXTENSION_INIT3 + +#ifdef _WIN32 +#include + +void* http_dlopen(const char* zName) { + return LoadLibraryA(zName); +} + +void http_dlclose(void* library) { + FreeLibrary(library); +} + +void* http_dlsym(void* library, const char* zName) { + return GetProcAddress(library, zName); +} +#else +#include + +void* http_dlopen(const char* zName) { + return dlopen(zName, RTLD_NOW); +} + +void http_dlclose(void* library) { + dlclose(zName); +} + +void* http_dlsym(void* library, const char* zName) { + return dlsym(library, zName); +} +#endif + +#ifndef MIN +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif + +typedef struct CURL CURL; +typedef int CURLcode; +typedef int CURLoption; +typedef int CURLINFO; +typedef int CURLversion; + +#define CURL_ERROR_SIZE 256 + +#define CURLE_OK 0 + +#define CURLOPT_ERRORBUFFER (10000 + 10) +#define CURLOPT_URL (10000 + 2) +#define CURLOPT_FOLLOWLOCATION (52) +#define CURLOPT_HTTPGET (80) +#define CURLOPT_POST (47) +#define CURLOPT_SSL_OPTIONS (216) +#define CURLOPT_WRITEFUNCTION (20000 + 11) +#define CURLOPT_WRITEDATA (10000 + 1) +#define CURLOPT_READFUNCTION (20000 + 12) +#define CURLOPT_READDATA (10000 + 9) +#define CURLOPT_HEADERFUNCTION (20000 + 79) +#define CURLOPT_HEADERDATA (10000 + 29) +#define CURLOPT_POSTFIELDS (10000 + 15) +#define CURLOPT_POSTFIELDSIZE_LARGE (30000 + 120) +#define CURLOPT_HTTPHEADER (10000 + 23) +#define CURLOPT_INFILESIZE_LARGE (30000 + 115) +#define CURLOPT_NOBODY (44) +#define CURLOPT_CUSTOMREQUEST (10000 + 36) +#define CURLOPT_PUT (54) + +#define CURLSSLOPT_NATIVE_CA (1 << 4) + +#define CURLVERSION_NOW 9 + +#define CURLINFO_RESPONSE_CODE (0x200000 + 2) + +// The struct is bigger but I don't need more information for now... +struct curl_version_info_data { + CURLversion age; + const char* version; + unsigned int version_num; +}; +typedef struct curl_version_info_data curl_version_info_data; + +struct curl_slist; + +typedef CURL* (*curl_easy_init_t)(); +typedef void (*curl_easy_cleanup_t)(CURL*); +typedef CURLcode (*curl_easy_setopt_t)(CURL*, CURLoption, ...); +typedef CURLcode (*curl_easy_perform_t)(CURL*); +typedef CURLcode (*curl_easy_getinfo_t)(CURL*, CURLINFO, ...); +typedef char* (*curl_version_t)(); +typedef curl_version_info_data* (*curl_version_info_t)(CURLversion); +typedef struct curl_slist* (*curl_slist_append_t)(struct curl_slist*, const char*); +typedef void (*curl_slist_free_all_t)(struct curl_slist*); +typedef const char* (*curl_easy_strerror_t)(CURLcode); + +struct curl_api_routines { + void* pLibrary; + curl_easy_init_t easy_init; + curl_easy_cleanup_t easy_cleanup; + curl_easy_setopt_t easy_setopt; + curl_easy_perform_t easy_perform; + curl_easy_getinfo_t easy_getinfo; + curl_version_t version; + curl_version_info_t version_info; + curl_slist_append_t slist_append; + curl_slist_free_all_t slist_free_all; + curl_easy_strerror_t easy_strerror; +}; + +static struct curl_api_routines curl_api; + +#define curl_easy_init curl_api.easy_init +#define curl_easy_cleanup curl_api.easy_cleanup +#define curl_easy_setopt curl_api.easy_setopt +#define curl_easy_perform curl_api.easy_perform +#define curl_easy_getinfo curl_api.easy_getinfo +#define curl_version curl_api.version +#define curl_version_info curl_api.version_info +#define curl_slist_append curl_api.slist_append +#define curl_slist_free_all curl_api.slist_free_all +#define curl_easy_strerror curl_api.easy_strerror + +static const char* aCurlLibNames[] = { +#ifdef _WIN32 + "libcurl-x64.dll", + "libcurl.dll", +#else + "libcurl.so", + "libcurl.so.4", +#endif +}; + +static const int szCurlLibNames = sizeof(aCurlLibNames) / sizeof(aCurlLibNames[0]); + +int http_backend_curl_load(char** zErrMsg) { + assert(zErrMsg != NULL); + + *zErrMsg = NULL; + + if (!curl_api.pLibrary) { + for (int i = 0; i < szCurlLibNames && !curl_api.pLibrary; ++i) { + curl_api.pLibrary = http_dlopen(aCurlLibNames[i]); + } + } + + if (!curl_api.pLibrary) { + *zErrMsg = sqlite3_mprintf("failed to load curl"); + goto error; + } + + curl_easy_init = (curl_easy_init_t)http_dlsym(curl_api.pLibrary, "curl_easy_init"); + if (!curl_easy_init) { + *zErrMsg = sqlite3_mprintf("failed to load curl_easy_init"); + goto error; + } + curl_easy_cleanup = (curl_easy_cleanup_t)http_dlsym(curl_api.pLibrary, "curl_easy_cleanup"); + if (!curl_easy_cleanup) { + *zErrMsg = sqlite3_mprintf("failed to load curl_easy_cleanup"); + goto error; + } + curl_easy_setopt = (curl_easy_setopt_t)http_dlsym(curl_api.pLibrary, "curl_easy_setopt"); + if (!curl_easy_setopt) { + *zErrMsg = sqlite3_mprintf("failed to load curl_easy_setopt"); + goto error; + } + curl_easy_perform = (curl_easy_perform_t)http_dlsym(curl_api.pLibrary, "curl_easy_perform"); + if (!curl_easy_perform) { + *zErrMsg = sqlite3_mprintf("failed to load curl_easy_perform"); + goto error; + } + curl_easy_getinfo = (curl_easy_getinfo_t)http_dlsym(curl_api.pLibrary, "curl_easy_getinfo"); + if (!curl_easy_getinfo) { + *zErrMsg = sqlite3_mprintf("failed to load curl_easy_getinfo"); + goto error; + } + curl_version = (curl_version_t)http_dlsym(curl_api.pLibrary, "curl_version"); + if (!curl_version) { + *zErrMsg = sqlite3_mprintf("failed to load curl_version"); + goto error; + } + curl_version_info = (curl_version_info_t)http_dlsym(curl_api.pLibrary, "curl_version_info"); + if (!curl_version_info) { + *zErrMsg = sqlite3_mprintf("failed to load curl_version_info"); + goto error; + } + curl_slist_append = (curl_slist_append_t)http_dlsym(curl_api.pLibrary, "curl_slist_append"); + if (!curl_slist_append) { + *zErrMsg = sqlite3_mprintf("failed to load curl_slist_append"); + goto error; + } + curl_slist_free_all = + (curl_slist_free_all_t)http_dlsym(curl_api.pLibrary, "curl_slist_free_all"); + if (!curl_slist_free_all) { + *zErrMsg = sqlite3_mprintf("failed to load curl_slist_free_all"); + goto error; + } + curl_easy_strerror = (curl_easy_strerror_t)http_dlsym(curl_api.pLibrary, "curl_easy_strerror"); + if (!curl_easy_strerror) { + *zErrMsg = sqlite3_mprintf("failed to load curl_easy_strerror"); + goto error; + } + + return SQLITE_OK; + +error: + + if (curl_api.pLibrary) { + http_dlclose(curl_api.pLibrary); + memset(&curl_api, 0, sizeof(curl_api)); + } + + return SQLITE_ERROR; +} + +static size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { + http_response* pResp = (http_response*)userdata; + char* p; + p = sqlite3_realloc(pResp->pBody, pResp->szBody + size * nmemb); + if (!p) { + return 0; + } + pResp->pBody = p; + memcpy((char*)pResp->pBody + pResp->szBody, ptr, size * nmemb); + pResp->szBody += size * nmemb; + return size * nmemb; +} + +static size_t header_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { + http_response* pResp = (http_response*)userdata; + char* p; + p = sqlite3_realloc(pResp->zHeaders, pResp->szHeaders + size * nmemb + 1); + if (!p) { + return 0; + } + pResp->zHeaders = p; + memcpy(pResp->zHeaders + pResp->szHeaders, ptr, size * nmemb); + pResp->szHeaders += size * nmemb; + pResp->zHeaders[pResp->szHeaders] = '\0'; + return size * nmemb; +} + +struct readdata { + const char* pBody; + size_t szBody; +}; + +static size_t read_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { + struct readdata* pData = (struct readdata*)userdata; + size_t szToSend = MIN(size * nmemb, pData->szBody); + memcpy(ptr, pData->pBody, szToSend); + pData->pBody += szToSend; + pData->szBody -= szToSend; + return szToSend; +} + +static int +headers_to_curl_headers(struct curl_slist** pHeaders, const char* zHeaders, int szHeaders) { + const char* name; + int szName; + const char* value; + int szValue; + while (szHeaders > 0) { + int nParsed = 0; + int rc = http_next_header(zHeaders, szHeaders, &nParsed, &name, &szName, &value, &szValue); + if (rc == SQLITE_DONE) { + break; + } else if (rc == SQLITE_ROW) { + char* zHeader = sqlite3_mprintf("%.*s: %.*s", szName, name, szValue, value); + if (!zHeader) { + return 0; + } + void* tmp = curl_slist_append(*pHeaders, zHeader); + sqlite3_free(zHeader); + if (!tmp) { + return 0; + } + *pHeaders = tmp; + szHeaders -= nParsed; + zHeaders += nParsed; + } else if (rc == SQLITE_ERROR) { + return 0; + } + } + return 1; +} + +static int set_curl_error_message(char** ppErrMsg, CURLcode rc, const char* message) { + *ppErrMsg = sqlite3_mprintf("%s: %s (curl error code %d)", message, curl_easy_strerror(rc), rc); + return SQLITE_ERROR; +} + +int http_do_request(http_request* req, http_response* resp, char** ppErrMsg) { + int rc; + CURL* curl; + char aErrorBuf[CURL_ERROR_SIZE]; + curl_version_info_data* pCurlVersionInfo; + long responseCode; + struct curl_slist* headers = NULL; + CURLcode curlrc; + struct readdata readdata; + + rc = http_backend_curl_load(ppErrMsg); + if (rc != SQLITE_OK) { + return rc; + } + + pCurlVersionInfo = curl_version_info(CURLVERSION_NOW); + + curl = curl_easy_init(); + if (!curl) { + *ppErrMsg = sqlite3_mprintf("curl_easy_init failed"); + rc = SQLITE_ERROR; + goto error; + } + + memset(aErrorBuf, 0, sizeof(aErrorBuf)); + if ((curlrc = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, aErrorBuf)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_URL, req->zUrl)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if (sqlite3_stricmp(req->zMethod, "GET") == 0) { + if ((curlrc = curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } else if (sqlite3_stricmp(req->zMethod, "POST") == 0) { + if ((curlrc = curl_easy_setopt(curl, CURLOPT_POST, 1L)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + if ((curlrc = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->pBody)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + if ((curlrc = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, req->szBody)) != + CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } else if (sqlite3_stricmp(req->zMethod, "PUT") == 0) { + if ((curlrc = curl_easy_setopt(curl, CURLOPT_PUT, 1L)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } else if (sqlite3_stricmp(req->zMethod, "HEAD") == 0) { + if ((curlrc = curl_easy_setopt(curl, CURLOPT_NOBODY, 1L)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } else { + if ((curlrc = curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if (req->pBody && req->szBody) { + if ((curlrc = curl_easy_setopt(curl, CURLOPT_PUT, 1L)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req->zMethod)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } + + if (req->pBody && req->szBody && sqlite3_stricmp(req->zMethod, "POST") != 0) { + readdata.pBody = (const char*)req->pBody; + readdata.szBody = (size_t)req->szBody; + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, req->szBody)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_READDATA, &readdata)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } + + // Since 7.71.0 (Jun 24 2020). Makes life easier on Windows at least. + if (pCurlVersionInfo->version_num >= ((7 << 16) | (71 << 8))) { + if ((curlrc = curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA)) != + CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + if ((curlrc = curl_easy_setopt(curl, CURLOPT_HEADERDATA, resp)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + + // Remove content-type header by default... + headers = curl_slist_append(NULL, "content-type;"); + if (!headers) { + *ppErrMsg = sqlite3_mprintf("curl_slist_append failed"); + rc = SQLITE_ERROR; + goto error; + } + + if (req->zHeaders) { + if (!headers_to_curl_headers(&headers, req->zHeaders, strlen(req->zHeaders))) { + *ppErrMsg = sqlite3_mprintf("failed to convert headers for curl"); + rc = SQLITE_ERROR; + goto error; + } + if ((curlrc = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers)) != CURLE_OK) { + rc = set_curl_error_message(ppErrMsg, curlrc, "curl_easy_setopt"); + goto error; + } + } + + if (curl_easy_perform(curl) != CURLE_OK) { + *ppErrMsg = sqlite3_mprintf("curl_easy_perform failed: %s", aErrorBuf); + rc = SQLITE_ERROR; + goto error; + } + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); + resp->iStatusCode = responseCode; + + remove_all_but_last_headers(resp->zHeaders); + separate_status_and_headers(&resp->zStatus, resp->zHeaders); + + rc = SQLITE_OK; + +error: + +done: + + curl_easy_cleanup(curl); + curl_slist_free_all(headers); + + return rc; +} + +#endif // HTTP_BACKEND_CURL + +/********** src/http_backend_dummy.c **********/ + +#ifdef HTTP_BACKEND_DUMMY + +#include "http.h" + +#include + +SQLITE_EXTENSION_INIT3 + +static http_request sLastRequest; +static http_response* sResponse; +static char* sErrMsg; + +void http_backend_dummy_set_response(http_response* response) { + sResponse = response; +} + +void http_backend_dummy_set_errmsg(const char* zErrMsg) { + sErrMsg = sqlite3_mprintf("%s", zErrMsg); +} + +void http_backend_dummy_reset_request() { + sqlite3_free(sLastRequest.zMethod); + sqlite3_free((void*)sLastRequest.pBody); + sqlite3_free((void*)sLastRequest.zHeaders); + memset(&sLastRequest, 0, sizeof(sLastRequest)); +} + +const http_request* http_backend_dummy_get_last_request() { + return &sLastRequest; +} + +int http_do_request(http_request* req, http_response* resp, char** ppErrMsg) { + int rc = SQLITE_OK; + if (sResponse) { + *resp = *sResponse; + sResponse = NULL; + } else if (sErrMsg) { + *ppErrMsg = sErrMsg; + sErrMsg = NULL; + rc = SQLITE_ERROR; + } else { + assert(0); + } + http_backend_dummy_reset_request(); + if (req->zMethod) { + sLastRequest.zMethod = sqlite3_mprintf("%s", req->zMethod); + } + if (req->zUrl) { + sLastRequest.zUrl = sqlite3_mprintf("%s", req->zUrl); + } + if (req->pBody) { + sLastRequest.pBody = sqlite3_malloc(req->szBody); + memcpy((void*)sLastRequest.pBody, req->pBody, req->szBody); + } + sLastRequest.szBody = req->szBody; + if (req->zHeaders) { + sLastRequest.zHeaders = sqlite3_mprintf("%s", req->zHeaders); + } + return rc; +} + +#endif // HTTP_BACKEND_DUMMY + +/********** src/http_backend_winhttp.c **********/ + +#ifdef HTTP_BACKEND_WINHTTP + +#include "http.h" + +#include +#include + +#include + +SQLITE_EXTENSION_INIT3 + +static char* unicode_to_utf8(LPCWSTR zWide) { + DWORD nSize; + char* zUtf8; + nSize = WideCharToMultiByte(CP_UTF8, 0, zWide, -1, NULL, 0, NULL, NULL); + if (nSize == 0) { + return NULL; + } + zUtf8 = sqlite3_malloc(nSize); + if (!zUtf8) { + return NULL; + } + nSize = WideCharToMultiByte(CP_UTF8, 0, zWide, -1, zUtf8, nSize, NULL, NULL); + if (nSize == 0) { + sqlite3_free(zUtf8); + return NULL; + } + return zUtf8; +} + +static LPWSTR utf8_to_unicode(const char* zText) { + DWORD nSize; + LPWSTR zWide; + nSize = MultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0); + if (nSize == 0) { + return NULL; + } + zWide = sqlite3_malloc(sizeof(*zWide) * nSize); + if (!zWide) { + return NULL; + } + nSize = MultiByteToWideChar(CP_UTF8, 0, zText, -1, zWide, nSize); + if (nSize == 0) { + sqlite3_free(zWide); + return NULL; + } + return zWide; +} + +static char* win32_get_last_error(DWORD code) { + LPWSTR zWide = NULL; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + code, + 0, + (LPWSTR)&zWide, + 0, + NULL); + char* zUtf8 = unicode_to_utf8(zWide); + LocalFree(zWide); + return zUtf8; +} + +static int query_headers(HINTERNET request, DWORD dwInfoLevel, char** ppResult) { + DWORD szWide = 0; + LPWSTR zWide = NULL; + + WinHttpQueryHeaders( + request, dwInfoLevel, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &szWide, WINHTTP_NO_HEADER_INDEX); + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + return SQLITE_ERROR; + } + + zWide = sqlite3_malloc(szWide); + if (!zWide) { + return SQLITE_NOMEM; + } + + if (!WinHttpQueryHeaders(request, + dwInfoLevel, + WINHTTP_HEADER_NAME_BY_INDEX, + zWide, + &szWide, + WINHTTP_NO_HEADER_INDEX)) { + sqlite3_free(zWide); + return SQLITE_ERROR; + } + + *ppResult = unicode_to_utf8(zWide); + + sqlite3_free(zWide); + + return SQLITE_OK; +} + +int http_do_request(http_request* req, http_response* resp, char** ppErrMsg) { + LPWSTR zUrlWide = NULL; + LPWSTR zHostWide = NULL; + LPWSTR zMethodWide = NULL; + LPWSTR zHeadersWide = NULL; + HINTERNET session = NULL; + HINTERNET conn = NULL; + HINTERNET request = NULL; + URL_COMPONENTS components; + DWORD lastErr = 0; + const char* errFunc = NULL; + char* zErrMsg = NULL; + int rc = SQLITE_ERROR; + LPWSTR zWideResponseHeaders = NULL; + DWORD szWideResponseHeaders = 0; + DWORD dwSize = 0; + DWORD dwStatusCode; + + zUrlWide = utf8_to_unicode(req->zUrl); + if (!zUrlWide) { + rc = SQLITE_NOMEM; + goto error; + } + + memset(&components, 0, sizeof(components)); + components.dwStructSize = sizeof(components); + components.dwHostNameLength = 1; + components.dwUrlPathLength = 1; + if (!WinHttpCrackUrl(zUrlWide, 0, 0, &components)) { + lastErr = GetLastError(); + errFunc = "WinHttpCrackUrl"; + goto error; + } + + session = WinHttpOpen(L"sqlite3-http", + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0); + + if (!session) { + lastErr = GetLastError(); + errFunc = "WinHttpCrackOpen"; + goto error; + } + + // WinHttpSetStatusCallback(session, + // (WINHTTP_STATUS_CALLBACK)statusCallback, + // WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, + // 0); + + zHostWide = sqlite3_malloc(sizeof(wchar_t) * (components.dwHostNameLength + 1)); + if (!zHostWide) { + rc = SQLITE_NOMEM; + goto error; + } + memcpy(zHostWide, components.lpszHostName, sizeof(wchar_t) * components.dwHostNameLength); + zHostWide[components.dwHostNameLength] = L'\0'; + + conn = WinHttpConnect(session, zHostWide, components.nPort, 0); + + if (!conn) { + lastErr = GetLastError(); + errFunc = "WinHttpConnect"; + goto error; + } + + zMethodWide = utf8_to_unicode(req->zMethod); + if (!zMethodWide) { + rc = SQLITE_NOMEM; + goto error; + } + + request = WinHttpOpenRequest(conn, + zMethodWide, + components.lpszUrlPath, + NULL, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + components.nPort == 443 ? WINHTTP_FLAG_SECURE : 0); + + if (!request) { + lastErr = GetLastError(); + errFunc = "WinHttpOpenRequest"; + goto error; + } + + if (req->zHeaders) { + zHeadersWide = utf8_to_unicode(req->zHeaders); + } + + if (!WinHttpSendRequest(request, + zHeadersWide, + zHeadersWide ? -1 : 0, + (void*)req->pBody, + req->szBody, + req->szBody, + 0)) { + lastErr = GetLastError(); + errFunc = "WinHttpSendRequest"; + goto error; + } + + if (!WinHttpReceiveResponse(request, NULL)) { + lastErr = GetLastError(); + errFunc = "WinHttpReceiveResponse"; + goto error; + } + + assert(resp->pBody == NULL); + assert(resp->szBody == 0); + for (;;) { + DWORD dwSize = 0; + DWORD nRead = 0; + char* nb; + if (!WinHttpQueryDataAvailable(request, &dwSize)) { + lastErr = GetLastError(); + errFunc = "WinHttpQueryDataAvailable"; + goto error; + } + if (dwSize == 0) { + break; + } + nb = sqlite3_realloc(resp->pBody, resp->szBody + dwSize); + if (!nb) { + rc = SQLITE_NOMEM; + goto error; + } + resp->pBody = nb; + if (!WinHttpReadData(request, (char*)resp->pBody + resp->szBody, dwSize, &nRead)) { + lastErr = GetLastError(); + errFunc = "WinHttpReadData"; + goto error; + } + resp->szBody += dwSize; + if (nRead == 0) { + break; + } + } + + rc = query_headers(request, WINHTTP_QUERY_RAW_HEADERS_CRLF, &resp->zHeaders); + if (rc != SQLITE_OK) { + if (rc == SQLITE_ERROR) { + lastErr = GetLastError(); + errFunc = "WinHttpQueryHeaders(WINHTTP_QUERY_RAW_HEADERS_CRLF)"; + } + goto error; + } + rc = SQLITE_ERROR; // need to reset this back. Otherwise a jump to error below + // could leave rc as SQLITE_OK... + + dwSize = sizeof(dwStatusCode); + if (!WinHttpQueryHeaders(request, + WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + &dwStatusCode, + &dwSize, + WINHTTP_NO_HEADER_INDEX)) { + lastErr = GetLastError(); + errFunc = "WinHttpQueryHeaders(WINHTTP_QUERY_STATUS_CODE)"; + goto error; + } + + resp->iStatusCode = dwStatusCode; + + remove_all_but_last_headers(resp->zHeaders); + separate_status_and_headers(&resp->zStatus, resp->zHeaders); + + rc = SQLITE_OK; + + goto done; + +error: + + if (rc == SQLITE_ERROR && errFunc) { + *ppErrMsg = win32_get_last_error(lastErr); + } + +done: + + sqlite3_free(zUrlWide); + sqlite3_free(zHostWide); + sqlite3_free(zMethodWide); + sqlite3_free(zHeadersWide); + WinHttpCloseHandle(request); + WinHttpCloseHandle(conn); + WinHttpCloseHandle(session); + + return rc; +} + +#endif // HTTP_BACKEND_WINHTTP + +/********** src/http_next_header.c **********/ + +#include +#include + +static int is_token_char(int c) { + return isalnum(c) || (c != 0 && strchr("!#$%&'*+-.^_`|~", c)); +} + +static int is_header_ws(int c) { + return c == ' ' || c == '\t'; +} + +static int is_vchar(int c) { + return c >= 0x21 && c <= 0x7e; +} + +// Return SQLITE_ROW if a header was extracted +// Return SQLITE_DONE if headers has been exhausted +// Return SQLITE_ERROR on malformed input +int http_next_header(const char* headers, + int size, + int* pParsed, + const char** ppName, + int* pNameSize, + const char** ppValue, + int* pValueSize) { + const char* headersEnd = headers + size; + const char* nameStart = NULL; + const char* nameEnd = NULL; + const char* valueStart = NULL; + const char* valueEnd = NULL; + const char* last = NULL; + const char* p = headers; + + enum { + StateInit, + StateName, + StateNameTrailingWs, + StateValueLeadingWs, + StateValue, + StateCR, + StateCheckFold, + StateDone, + StateError, + }; + + int state = StateInit; + + *ppName = NULL; + *pNameSize = 0; + *ppValue = NULL; + *pValueSize = 0; + + if (size == 0) { + *pParsed = 0; + return SQLITE_DONE; + } + + for (; p != headersEnd && state != StateDone && state != StateError; ++p) { + switch (state) { + case StateInit: + // printf("StateInit %02x %c\n", *p, isalnum(*p) ? *p : ' '); + if (*p == '\r') { + state = StateCR; + } else if (is_header_ws(*p)) { + // There shouldn't be whitespace here. Folding is handled in + // StateCR/StateCheckFold. + state = StateError; + } else { + nameStart = p; + state = StateName; + } + break; + + case StateName: + // printf("StateName %02x %c\n", *p, isalnum(*p) ? *p : ' '); + if (is_token_char(*p)) { + // accept + } else if (*p == ':') { + nameEnd = p; + state = StateValueLeadingWs; + } else if (is_header_ws(*p)) { + nameEnd = p; + state = StateNameTrailingWs; + } else { + state = StateError; + } + break; + + case StateNameTrailingWs: + // printf("StateNameTrailingWs %02x %c\n", *p, isalnum(*p) ? *p : ' '); + if (is_header_ws(*p)) { + // accept and skip + } else if (*p == ':') { + state = StateValueLeadingWs; + } else { + state = StateError; + } + break; + + case StateValueLeadingWs: + // printf("StateValueLeadingWs %02x %c\n", *p, isalnum(*p) ? *p : ' '); + if (is_header_ws(*p)) { + // accept and skip + } else if (*p == '\r') { + // empty value + state = StateCR; + } else if (is_vchar(*p)) { + valueStart = p; + last = p; + state = StateValue; + } else { + state = StateError; + } + break; + + case StateValue: + // printf("StateValue %02x %c\n", *p, isalnum(*p) ? *p : ' '); + if (is_header_ws(*p)) { + // accept but keep separate so that we can track trailing ws + } else if (*p == '\r') { + valueEnd = last + 1; + state = StateCR; + } else if (is_vchar(*p)) { + // accept + last = p; + } + break; + + case StateCR: + // printf("StateCr %02x %c\n", *p, isalnum(*p) ? *p : ' '); + if (*p == '\n') { + // If there's more data, check for folding. + if (p + 1 == headers + size) { + state = StateDone; + } else { + state = StateCheckFold; + } + } else { + state = StateError; + } + break; + + case StateCheckFold: + // printf("StateCheckFold %02x %c\n", *p, isalnum(*p) ? *p : ' '); + if (is_header_ws(*p)) { + state = StateValue; + } else { + state = StateDone; + --p; + } + break; + } + } + + *pParsed = p - headers; + + if (state != StateDone || state == StateError) { + return SQLITE_ERROR; + } + + if (!nameStart) { + return SQLITE_DONE; + } + + *ppName = nameStart; + *pNameSize = nameEnd - nameStart; + + if (valueStart) { + *ppValue = valueStart; + *pValueSize = valueEnd - valueStart; + } + + return SQLITE_ROW; +} diff --git a/thirdparty/sqlite-amalgamation/shell.c b/thirdparty/sqlite-amalgamation/shell.c new file mode 100644 index 0000000..e66ae08 --- /dev/null +++ b/thirdparty/sqlite-amalgamation/shell.c @@ -0,0 +1,23795 @@ +/* DO NOT EDIT! +** This file is automatically generated by the script in the canonical +** SQLite source tree at tool/mkshellc.tcl. That script combines source +** code from various constituent source files of SQLite into this single +** "shell.c" file used to implement the SQLite command-line shell. +** +** Most of the code found below comes from the "src/shell.c.in" file in +** the canonical SQLite source tree. That main file contains "INCLUDE" +** lines that specify other files in the canonical source tree that are +** inserted to getnerate this complete program source file. +** +** The code from multiple files is combined into this single "shell.c" +** source file to help make the command-line program easier to compile. +** +** To modify this program, get a copy of the canonical SQLite source tree, +** edit the src/shell.c.in" and/or some of the other files that are included +** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script. +*/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code to implement the "sqlite" command line +** utility for accessing SQLite databases. +*/ +#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) +/* This needs to come before any includes for MSVC compiler */ +#define _CRT_SECURE_NO_WARNINGS +#endif + +/* +** Optionally #include a user-defined header, whereby compilation options +** may be set prior to where they take effect, but after platform setup. +** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include +** file. Note that this macro has a like effect on sqlite3.c compilation. +*/ +# define SHELL_STRINGIFY_(f) #f +# define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f) +#ifdef SQLITE_CUSTOM_INCLUDE +# include SHELL_STRINGIFY(SQLITE_CUSTOM_INCLUDE) +#endif + +/* +** Determine if we are dealing with WinRT, which provides only a subset of +** the full Win32 API. +*/ +#if !defined(SQLITE_OS_WINRT) +# define SQLITE_OS_WINRT 0 +#endif + +/* +** Warning pragmas copied from msvc.h in the core. +*/ +#if defined(_MSC_VER) +#pragma warning(disable : 4054) +#pragma warning(disable : 4055) +#pragma warning(disable : 4100) +#pragma warning(disable : 4127) +#pragma warning(disable : 4130) +#pragma warning(disable : 4152) +#pragma warning(disable : 4189) +#pragma warning(disable : 4206) +#pragma warning(disable : 4210) +#pragma warning(disable : 4232) +#pragma warning(disable : 4244) +#pragma warning(disable : 4305) +#pragma warning(disable : 4306) +#pragma warning(disable : 4702) +#pragma warning(disable : 4706) +#endif /* defined(_MSC_VER) */ + +/* +** No support for loadable extensions in VxWorks. +*/ +#if (defined(__RTP__) || defined(_WRS_KERNEL)) && !SQLITE_OMIT_LOAD_EXTENSION +# define SQLITE_OMIT_LOAD_EXTENSION 1 +#endif + +/* +** Enable large-file support for fopen() and friends on unix. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + +#include +#include +#include +#include +#include "sqlite3.h" +typedef sqlite3_int64 i64; +typedef sqlite3_uint64 u64; +typedef unsigned char u8; +#if SQLITE_USER_AUTHENTICATION +# include "sqlite3userauth.h" +#endif +#include +#include + +#if !defined(_WIN32) && !defined(WIN32) +# include +# if !defined(__RTP__) && !defined(_WRS_KERNEL) +# include +# endif +#endif +#if (!defined(_WIN32) && !defined(WIN32)) || defined(__MINGW32__) +# include +# include +# define GETPID getpid +# if defined(__MINGW32__) +# define DIRENT dirent +# ifndef S_ISLNK +# define S_ISLNK(mode) (0) +# endif +# endif +#else +# define GETPID (int)GetCurrentProcessId +#endif +#include +#include + +#if HAVE_READLINE +# include +# include +#endif + +#if HAVE_EDITLINE +# include +#endif + +#if HAVE_EDITLINE || HAVE_READLINE + +# define shell_add_history(X) add_history(X) +# define shell_read_history(X) read_history(X) +# define shell_write_history(X) write_history(X) +# define shell_stifle_history(X) stifle_history(X) +# define shell_readline(X) readline(X) + +#elif HAVE_LINENOISE + +# include "linenoise.h" +# define shell_add_history(X) linenoiseHistoryAdd(X) +# define shell_read_history(X) linenoiseHistoryLoad(X) +# define shell_write_history(X) linenoiseHistorySave(X) +# define shell_stifle_history(X) linenoiseHistorySetMaxLen(X) +# define shell_readline(X) linenoise(X) + +#else + +# define shell_read_history(X) +# define shell_write_history(X) +# define shell_stifle_history(X) + +# define SHELL_USE_LOCAL_GETLINE 1 +#endif + + +#if defined(_WIN32) || defined(WIN32) +# if SQLITE_OS_WINRT +# define SQLITE_OMIT_POPEN 1 +# else +# include +# include +# define isatty(h) _isatty(h) +# ifndef access +# define access(f,m) _access((f),(m)) +# endif +# ifndef unlink +# define unlink _unlink +# endif +# ifndef strdup +# define strdup _strdup +# endif +# undef popen +# define popen _popen +# undef pclose +# define pclose _pclose +# endif +#else + /* Make sure isatty() has a prototype. */ + extern int isatty(int); + +# if !defined(__RTP__) && !defined(_WRS_KERNEL) + /* popen and pclose are not C89 functions and so are + ** sometimes omitted from the header */ + extern FILE *popen(const char*,const char*); + extern int pclose(FILE*); +# else +# define SQLITE_OMIT_POPEN 1 +# endif +#endif + +#if defined(_WIN32_WCE) +/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() + * thus we always assume that we have a console. That can be + * overridden with the -batch command line option. + */ +#define isatty(x) 1 +#endif + +/* ctype macros that work with signed characters */ +#define IsSpace(X) isspace((unsigned char)X) +#define IsDigit(X) isdigit((unsigned char)X) +#define ToLower(X) (char)tolower((unsigned char)X) + +#if defined(_WIN32) || defined(WIN32) +#if SQLITE_OS_WINRT +#include +#endif +#include + +/* string conversion routines only needed on Win32 */ +extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); +extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); +extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); +extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); +#endif + +/* On Windows, we normally run with output mode of TEXT so that \n characters +** are automatically translated into \r\n. However, this behavior needs +** to be disabled in some cases (ex: when generating CSV output and when +** rendering quoted strings that contain \n characters). The following +** routines take care of that. +*/ +#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT +static void setBinaryMode(FILE *file, int isOutput){ + if( isOutput ) fflush(file); + _setmode(_fileno(file), _O_BINARY); +} +static void setTextMode(FILE *file, int isOutput){ + if( isOutput ) fflush(file); + _setmode(_fileno(file), _O_TEXT); +} +#else +# define setBinaryMode(X,Y) +# define setTextMode(X,Y) +#endif + +/* +** When compiling with emcc (a.k.a. emscripten), we're building a +** WebAssembly (WASM) bundle and need to disable and rewire a few +** things. +*/ +#ifdef __EMSCRIPTEN__ +#define SQLITE_SHELL_WASM_MODE +#else +#undef SQLITE_SHELL_WASM_MODE +#endif + +/* True if the timer is enabled */ +static int enableTimer = 0; + +/* Return the current wall-clock time */ +static sqlite3_int64 timeOfDay(void){ + static sqlite3_vfs *clockVfs = 0; + sqlite3_int64 t; + if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); + if( clockVfs==0 ) return 0; /* Never actually happens */ + if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ + clockVfs->xCurrentTimeInt64(clockVfs, &t); + }else{ + double r; + clockVfs->xCurrentTime(clockVfs, &r); + t = (sqlite3_int64)(r*86400000.0); + } + return t; +} + +#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) +#include +#include + +/* VxWorks does not support getrusage() as far as we can determine */ +#if defined(_WRS_KERNEL) || defined(__RTP__) +struct rusage { + struct timeval ru_utime; /* user CPU time used */ + struct timeval ru_stime; /* system CPU time used */ +}; +#define getrusage(A,B) memset(B,0,sizeof(*B)) +#endif + +/* Saved resource information for the beginning of an operation */ +static struct rusage sBegin; /* CPU time at start */ +static sqlite3_int64 iBegin; /* Wall-clock time at start */ + +/* +** Begin timing an operation +*/ +static void beginTimer(void){ + if( enableTimer ){ + getrusage(RUSAGE_SELF, &sBegin); + iBegin = timeOfDay(); + } +} + +/* Return the difference of two time_structs in seconds */ +static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ + return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + + (double)(pEnd->tv_sec - pStart->tv_sec); +} + +/* +** Print the timing results. +*/ +static void endTimer(void){ + if( enableTimer ){ + sqlite3_int64 iEnd = timeOfDay(); + struct rusage sEnd; + getrusage(RUSAGE_SELF, &sEnd); + printf("Run Time: real %.3f user %f sys %f\n", + (iEnd - iBegin)*0.001, + timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), + timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); + } +} + +#define BEGIN_TIMER beginTimer() +#define END_TIMER endTimer() +#define HAS_TIMER 1 + +#elif (defined(_WIN32) || defined(WIN32)) + +/* Saved resource information for the beginning of an operation */ +static HANDLE hProcess; +static FILETIME ftKernelBegin; +static FILETIME ftUserBegin; +static sqlite3_int64 ftWallBegin; +typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, + LPFILETIME, LPFILETIME); +static GETPROCTIMES getProcessTimesAddr = NULL; + +/* +** Check to see if we have timer support. Return 1 if necessary +** support found (or found previously). +*/ +static int hasTimer(void){ + if( getProcessTimesAddr ){ + return 1; + } else { +#if !SQLITE_OS_WINRT + /* GetProcessTimes() isn't supported in WIN95 and some other Windows + ** versions. See if the version we are running on has it, and if it + ** does, save off a pointer to it and the current process handle. + */ + hProcess = GetCurrentProcess(); + if( hProcess ){ + HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); + if( NULL != hinstLib ){ + getProcessTimesAddr = + (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); + if( NULL != getProcessTimesAddr ){ + return 1; + } + FreeLibrary(hinstLib); + } + } +#endif + } + return 0; +} + +/* +** Begin timing an operation +*/ +static void beginTimer(void){ + if( enableTimer && getProcessTimesAddr ){ + FILETIME ftCreation, ftExit; + getProcessTimesAddr(hProcess,&ftCreation,&ftExit, + &ftKernelBegin,&ftUserBegin); + ftWallBegin = timeOfDay(); + } +} + +/* Return the difference of two FILETIME structs in seconds */ +static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ + sqlite_int64 i64Start = *((sqlite_int64 *) pStart); + sqlite_int64 i64End = *((sqlite_int64 *) pEnd); + return (double) ((i64End - i64Start) / 10000000.0); +} + +/* +** Print the timing results. +*/ +static void endTimer(void){ + if( enableTimer && getProcessTimesAddr){ + FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; + sqlite3_int64 ftWallEnd = timeOfDay(); + getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); + printf("Run Time: real %.3f user %f sys %f\n", + (ftWallEnd - ftWallBegin)*0.001, + timeDiff(&ftUserBegin, &ftUserEnd), + timeDiff(&ftKernelBegin, &ftKernelEnd)); + } +} + +#define BEGIN_TIMER beginTimer() +#define END_TIMER endTimer() +#define HAS_TIMER hasTimer() + +#else +#define BEGIN_TIMER +#define END_TIMER +#define HAS_TIMER 0 +#endif + +/* +** Used to prevent warnings about unused parameters +*/ +#define UNUSED_PARAMETER(x) (void)(x) + +/* +** Number of elements in an array +*/ +#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) + +/* +** If the following flag is set, then command execution stops +** at an error if we are not interactive. +*/ +static int bail_on_error = 0; + +/* +** Threat stdin as an interactive input if the following variable +** is true. Otherwise, assume stdin is connected to a file or pipe. +*/ +static int stdin_is_interactive = 1; + +/* +** On Windows systems we have to know if standard output is a console +** in order to translate UTF-8 into MBCS. The following variable is +** true if translation is required. +*/ +static int stdout_is_console = 1; + +/* +** The following is the open SQLite database. We make a pointer +** to this database a static variable so that it can be accessed +** by the SIGINT handler to interrupt database processing. +*/ +static sqlite3 *globalDb = 0; + +/* +** True if an interrupt (Control-C) has been received. +*/ +static volatile int seenInterrupt = 0; + +/* +** This is the name of our program. It is set in main(), used +** in a number of other places, mostly for error messages. +*/ +static char *Argv0; + +/* +** Prompt strings. Initialized in main. Settable with +** .prompt main continue +*/ +static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ +static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ + +/* +** Render output like fprintf(). Except, if the output is going to the +** console and if this is running on a Windows machine, translate the +** output from UTF-8 into MBCS. +*/ +#if defined(_WIN32) || defined(WIN32) +void utf8_printf(FILE *out, const char *zFormat, ...){ + va_list ap; + va_start(ap, zFormat); + if( stdout_is_console && (out==stdout || out==stderr) ){ + char *z1 = sqlite3_vmprintf(zFormat, ap); + char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); + sqlite3_free(z1); + fputs(z2, out); + sqlite3_free(z2); + }else{ + vfprintf(out, zFormat, ap); + } + va_end(ap); +} +#elif !defined(utf8_printf) +# define utf8_printf fprintf +#endif + +/* +** Render output like fprintf(). This should not be used on anything that +** includes string formatting (e.g. "%s"). +*/ +#if !defined(raw_printf) +# define raw_printf fprintf +#endif + +/* Indicate out-of-memory and exit. */ +static void shell_out_of_memory(void){ + raw_printf(stderr,"Error: out of memory\n"); + exit(1); +} + +/* Check a pointer to see if it is NULL. If it is NULL, exit with an +** out-of-memory error. +*/ +static void shell_check_oom(void *p){ + if( p==0 ) shell_out_of_memory(); +} + +/* +** Write I/O traces to the following stream. +*/ +#ifdef SQLITE_ENABLE_IOTRACE +static FILE *iotrace = 0; +#endif + +/* +** This routine works like printf in that its first argument is a +** format string and subsequent arguments are values to be substituted +** in place of % fields. The result of formatting this string +** is written to iotrace. +*/ +#ifdef SQLITE_ENABLE_IOTRACE +static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ + va_list ap; + char *z; + if( iotrace==0 ) return; + va_start(ap, zFormat); + z = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + utf8_printf(iotrace, "%s", z); + sqlite3_free(z); +} +#endif + +/* +** Output string zUtf to stream pOut as w characters. If w is negative, +** then right-justify the text. W is the width in UTF-8 characters, not +** in bytes. This is different from the %*.*s specification in printf +** since with %*.*s the width is measured in bytes, not characters. +*/ +static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ + int i; + int n; + int aw = w<0 ? -w : w; + for(i=n=0; zUtf[i]; i++){ + if( (zUtf[i]&0xc0)!=0x80 ){ + n++; + if( n==aw ){ + do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); + break; + } + } + } + if( n>=aw ){ + utf8_printf(pOut, "%.*s", i, zUtf); + }else if( w<0 ){ + utf8_printf(pOut, "%*s%s", aw-n, "", zUtf); + }else{ + utf8_printf(pOut, "%s%*s", zUtf, aw-n, ""); + } +} + + +/* +** Determines if a string is a number of not. +*/ +static int isNumber(const char *z, int *realnum){ + if( *z=='-' || *z=='+' ) z++; + if( !IsDigit(*z) ){ + return 0; + } + z++; + if( realnum ) *realnum = 0; + while( IsDigit(*z) ){ z++; } + if( *z=='.' ){ + z++; + if( !IsDigit(*z) ) return 0; + while( IsDigit(*z) ){ z++; } + if( realnum ) *realnum = 1; + } + if( *z=='e' || *z=='E' ){ + z++; + if( *z=='+' || *z=='-' ) z++; + if( !IsDigit(*z) ) return 0; + while( IsDigit(*z) ){ z++; } + if( realnum ) *realnum = 1; + } + return *z==0; +} + +/* +** Compute a string length that is limited to what can be stored in +** lower 30 bits of a 32-bit signed integer. +*/ +static int strlen30(const char *z){ + const char *z2 = z; + while( *z2 ){ z2++; } + return 0x3fffffff & (int)(z2 - z); +} + +/* +** Return the length of a string in characters. Multibyte UTF8 characters +** count as a single character. +*/ +static int strlenChar(const char *z){ + int n = 0; + while( *z ){ + if( (0xc0&*(z++))!=0x80 ) n++; + } + return n; +} + +/* +** Return open FILE * if zFile exists, can be opened for read +** and is an ordinary file or a character stream source. +** Otherwise return 0. +*/ +static FILE * openChrSource(const char *zFile){ +#ifdef _WIN32 + struct _stat x = {0}; +# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) + /* On Windows, open first, then check the stream nature. This order + ** is necessary because _stat() and sibs, when checking a named pipe, + ** effectively break the pipe as its supplier sees it. */ + FILE *rv = fopen(zFile, "rb"); + if( rv==0 ) return 0; + if( _fstat(_fileno(rv), &x) != 0 + || !STAT_CHR_SRC(x.st_mode)){ + fclose(rv); + rv = 0; + } + return rv; +#else + struct stat x = {0}; + int rc = stat(zFile, &x); +# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) + if( rc!=0 ) return 0; + if( STAT_CHR_SRC(x.st_mode) ){ + return fopen(zFile, "rb"); + }else{ + return 0; + } +#endif +#undef STAT_CHR_SRC +} + +/* +** This routine reads a line of text from FILE in, stores +** the text in memory obtained from malloc() and returns a pointer +** to the text. NULL is returned at end of file, or if malloc() +** fails. +** +** If zLine is not NULL then it is a malloced buffer returned from +** a previous call to this routine that may be reused. +*/ +static char *local_getline(char *zLine, FILE *in){ + int nLine = zLine==0 ? 0 : 100; + int n = 0; + + while( 1 ){ + if( n+100>nLine ){ + nLine = nLine*2 + 100; + zLine = realloc(zLine, nLine); + shell_check_oom(zLine); + } + if( fgets(&zLine[n], nLine - n, in)==0 ){ + if( n==0 ){ + free(zLine); + return 0; + } + zLine[n] = 0; + break; + } + while( zLine[n] ) n++; + if( n>0 && zLine[n-1]=='\n' ){ + n--; + if( n>0 && zLine[n-1]=='\r' ) n--; + zLine[n] = 0; + break; + } + } +#if defined(_WIN32) || defined(WIN32) + /* For interactive input on Windows systems, translate the + ** multi-byte characterset characters into UTF-8. */ + if( stdin_is_interactive && in==stdin ){ + char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); + if( zTrans ){ + int nTrans = strlen30(zTrans)+1; + if( nTrans>nLine ){ + zLine = realloc(zLine, nTrans); + shell_check_oom(zLine); + } + memcpy(zLine, zTrans, nTrans); + sqlite3_free(zTrans); + } + } +#endif /* defined(_WIN32) || defined(WIN32) */ + return zLine; +} + +/* +** Retrieve a single line of input text. +** +** If in==0 then read from standard input and prompt before each line. +** If isContinuation is true, then a continuation prompt is appropriate. +** If isContinuation is zero, then the main prompt should be used. +** +** If zPrior is not NULL then it is a buffer from a prior call to this +** routine that can be reused. +** +** The result is stored in space obtained from malloc() and must either +** be freed by the caller or else passed back into this routine via the +** zPrior argument for reuse. +*/ +#ifndef SQLITE_SHELL_WASM_MODE +static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ + char *zPrompt; + char *zResult; + if( in!=0 ){ + zResult = local_getline(zPrior, in); + }else{ + zPrompt = isContinuation ? continuePrompt : mainPrompt; +#if SHELL_USE_LOCAL_GETLINE + printf("%s", zPrompt); + fflush(stdout); + zResult = local_getline(zPrior, stdin); +#else + free(zPrior); + zResult = shell_readline(zPrompt); + if( zResult && *zResult ) shell_add_history(zResult); +#endif + } + return zResult; +} +#endif /* !SQLITE_SHELL_WASM_MODE */ + +/* +** Return the value of a hexadecimal digit. Return -1 if the input +** is not a hex digit. +*/ +static int hexDigitValue(char c){ + if( c>='0' && c<='9' ) return c - '0'; + if( c>='a' && c<='f' ) return c - 'a' + 10; + if( c>='A' && c<='F' ) return c - 'A' + 10; + return -1; +} + +/* +** Interpret zArg as an integer value, possibly with suffixes. +*/ +static sqlite3_int64 integerValue(const char *zArg){ + sqlite3_int64 v = 0; + static const struct { char *zSuffix; int iMult; } aMult[] = { + { "KiB", 1024 }, + { "MiB", 1024*1024 }, + { "GiB", 1024*1024*1024 }, + { "KB", 1000 }, + { "MB", 1000000 }, + { "GB", 1000000000 }, + { "K", 1000 }, + { "M", 1000000 }, + { "G", 1000000000 }, + }; + int i; + int isNeg = 0; + if( zArg[0]=='-' ){ + isNeg = 1; + zArg++; + }else if( zArg[0]=='+' ){ + zArg++; + } + if( zArg[0]=='0' && zArg[1]=='x' ){ + int x; + zArg += 2; + while( (x = hexDigitValue(zArg[0]))>=0 ){ + v = (v<<4) + x; + zArg++; + } + }else{ + while( IsDigit(zArg[0]) ){ + v = v*10 + zArg[0] - '0'; + zArg++; + } + } + for(i=0; iz); + initText(p); +} + +/* zIn is either a pointer to a NULL-terminated string in memory obtained +** from malloc(), or a NULL pointer. The string pointed to by zAppend is +** added to zIn, and the result returned in memory obtained from malloc(). +** zIn, if it was not NULL, is freed. +** +** If the third argument, quote, is not '\0', then it is used as a +** quote character for zAppend. +*/ +static void appendText(ShellText *p, const char *zAppend, char quote){ + int len; + int i; + int nAppend = strlen30(zAppend); + + len = nAppend+p->n+1; + if( quote ){ + len += 2; + for(i=0; iz==0 || p->n+len>=p->nAlloc ){ + p->nAlloc = p->nAlloc*2 + len + 20; + p->z = realloc(p->z, p->nAlloc); + shell_check_oom(p->z); + } + + if( quote ){ + char *zCsr = p->z+p->n; + *zCsr++ = quote; + for(i=0; in = (int)(zCsr - p->z); + *zCsr = '\0'; + }else{ + memcpy(p->z+p->n, zAppend, nAppend); + p->n += nAppend; + p->z[p->n] = '\0'; + } +} + +/* +** Attempt to determine if identifier zName needs to be quoted, either +** because it contains non-alphanumeric characters, or because it is an +** SQLite keyword. Be conservative in this estimate: When in doubt assume +** that quoting is required. +** +** Return '"' if quoting is required. Return 0 if no quoting is required. +*/ +static char quoteChar(const char *zName){ + int i; + if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; + for(i=0; zName[i]; i++){ + if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; + } + return sqlite3_keyword_check(zName, i) ? '"' : 0; +} + +/* +** Construct a fake object name and column list to describe the structure +** of the view, virtual table, or table valued function zSchema.zName. +*/ +static char *shellFakeSchema( + sqlite3 *db, /* The database connection containing the vtab */ + const char *zSchema, /* Schema of the database holding the vtab */ + const char *zName /* The name of the virtual table */ +){ + sqlite3_stmt *pStmt = 0; + char *zSql; + ShellText s; + char cQuote; + char *zDiv = "("; + int nRow = 0; + + zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;", + zSchema ? zSchema : "main", zName); + shell_check_oom(zSql); + sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + initText(&s); + if( zSchema ){ + cQuote = quoteChar(zSchema); + if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0; + appendText(&s, zSchema, cQuote); + appendText(&s, ".", 0); + } + cQuote = quoteChar(zName); + appendText(&s, zName, cQuote); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + const char *zCol = (const char*)sqlite3_column_text(pStmt, 1); + nRow++; + appendText(&s, zDiv, 0); + zDiv = ","; + if( zCol==0 ) zCol = ""; + cQuote = quoteChar(zCol); + appendText(&s, zCol, cQuote); + } + appendText(&s, ")", 0); + sqlite3_finalize(pStmt); + if( nRow==0 ){ + freeText(&s); + s.z = 0; + } + return s.z; +} + +/* +** SQL function: shell_module_schema(X) +** +** Return a fake schema for the table-valued function or eponymous virtual +** table X. +*/ +static void shellModuleSchema( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + const char *zName; + char *zFake; + UNUSED_PARAMETER(nVal); + zName = (const char*)sqlite3_value_text(apVal[0]); + zFake = zName ? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0; + if( zFake ){ + sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake), + -1, sqlite3_free); + free(zFake); + } +} + +/* +** SQL function: shell_add_schema(S,X) +** +** Add the schema name X to the CREATE statement in S and return the result. +** Examples: +** +** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); +** +** Also works on +** +** CREATE INDEX +** CREATE UNIQUE INDEX +** CREATE VIEW +** CREATE TRIGGER +** CREATE VIRTUAL TABLE +** +** This UDF is used by the .schema command to insert the schema name of +** attached databases into the middle of the sqlite_schema.sql field. +*/ +static void shellAddSchemaName( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + static const char *aPrefix[] = { + "TABLE", + "INDEX", + "UNIQUE INDEX", + "VIEW", + "TRIGGER", + "VIRTUAL TABLE" + }; + int i = 0; + const char *zIn = (const char*)sqlite3_value_text(apVal[0]); + const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); + const char *zName = (const char*)sqlite3_value_text(apVal[2]); + sqlite3 *db = sqlite3_context_db_handle(pCtx); + UNUSED_PARAMETER(nVal); + if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){ + for(i=0; i +#include +#include +#include +#include +#include +#include + +/* +** We may need several defines that should have been in "sys/stat.h". +*/ + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#ifndef S_ISLNK +#define S_ISLNK(mode) (0) +#endif + +/* +** We may need to provide the "mode_t" type. +*/ + +#ifndef MODE_T_DEFINED + #define MODE_T_DEFINED + typedef unsigned short mode_t; +#endif + +/* +** We may need to provide the "ino_t" type. +*/ + +#ifndef INO_T_DEFINED + #define INO_T_DEFINED + typedef unsigned short ino_t; +#endif + +/* +** We need to define "NAME_MAX" if it was not present in "limits.h". +*/ + +#ifndef NAME_MAX +# ifdef FILENAME_MAX +# define NAME_MAX (FILENAME_MAX) +# else +# define NAME_MAX (260) +# endif +#endif + +/* +** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T". +*/ + +#ifndef NULL_INTPTR_T +# define NULL_INTPTR_T ((intptr_t)(0)) +#endif + +#ifndef BAD_INTPTR_T +# define BAD_INTPTR_T ((intptr_t)(-1)) +#endif + +/* +** We need to provide the necessary structures and related types. +*/ + +#ifndef DIRENT_DEFINED +#define DIRENT_DEFINED +typedef struct DIRENT DIRENT; +typedef DIRENT *LPDIRENT; +struct DIRENT { + ino_t d_ino; /* Sequence number, do not use. */ + unsigned d_attributes; /* Win32 file attributes. */ + char d_name[NAME_MAX + 1]; /* Name within the directory. */ +}; +#endif + +#ifndef DIR_DEFINED +#define DIR_DEFINED +typedef struct DIR DIR; +typedef DIR *LPDIR; +struct DIR { + intptr_t d_handle; /* Value returned by "_findfirst". */ + DIRENT d_first; /* DIRENT constructed based on "_findfirst". */ + DIRENT d_next; /* DIRENT constructed based on "_findnext". */ +}; +#endif + +/* +** Provide a macro, for use by the implementation, to determine if a +** particular directory entry should be skipped over when searching for +** the next directory entry that should be returned by the readdir() or +** readdir_r() functions. +*/ + +#ifndef is_filtered +# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM)) +#endif + +/* +** Provide the function prototype for the POSIX compatiable getenv() +** function. This function is not thread-safe. +*/ + +extern const char *windirent_getenv(const char *name); + +/* +** Finally, we can provide the function prototypes for the opendir(), +** readdir(), readdir_r(), and closedir() POSIX functions. +*/ + +extern LPDIR opendir(const char *dirname); +extern LPDIRENT readdir(LPDIR dirp); +extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result); +extern INT closedir(LPDIR dirp); + +#endif /* defined(WIN32) && defined(_MSC_VER) */ + +/************************* End test_windirent.h ********************/ +/************************* Begin test_windirent.c ******************/ +/* +** 2015 November 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code to implement most of the opendir() family of +** POSIX functions on Win32 using the MSVCRT. +*/ + +#if defined(_WIN32) && defined(_MSC_VER) +/* #include "test_windirent.h" */ + +/* +** Implementation of the POSIX getenv() function using the Win32 API. +** This function is not thread-safe. +*/ +const char *windirent_getenv( + const char *name +){ + static char value[32768]; /* Maximum length, per MSDN */ + DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */ + DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */ + + memset(value, 0, sizeof(value)); + dwRet = GetEnvironmentVariableA(name, value, dwSize); + if( dwRet==0 || dwRet>dwSize ){ + /* + ** The function call to GetEnvironmentVariableA() failed -OR- + ** the buffer is not large enough. Either way, return NULL. + */ + return 0; + }else{ + /* + ** The function call to GetEnvironmentVariableA() succeeded + ** -AND- the buffer contains the entire value. + */ + return value; + } +} + +/* +** Implementation of the POSIX opendir() function using the MSVCRT. +*/ +LPDIR opendir( + const char *dirname +){ + struct _finddata_t data; + LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR)); + SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]); + + if( dirp==NULL ) return NULL; + memset(dirp, 0, sizeof(DIR)); + + /* TODO: Remove this if Unix-style root paths are not used. */ + if( sqlite3_stricmp(dirname, "/")==0 ){ + dirname = windirent_getenv("SystemDrive"); + } + + memset(&data, 0, sizeof(struct _finddata_t)); + _snprintf(data.name, namesize, "%s\\*", dirname); + dirp->d_handle = _findfirst(data.name, &data); + + if( dirp->d_handle==BAD_INTPTR_T ){ + closedir(dirp); + return NULL; + } + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ){ +next: + + memset(&data, 0, sizeof(struct _finddata_t)); + if( _findnext(dirp->d_handle, &data)==-1 ){ + closedir(dirp); + return NULL; + } + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ) goto next; + } + + dirp->d_first.d_attributes = data.attrib; + strncpy(dirp->d_first.d_name, data.name, NAME_MAX); + dirp->d_first.d_name[NAME_MAX] = '\0'; + + return dirp; +} + +/* +** Implementation of the POSIX readdir() function using the MSVCRT. +*/ +LPDIRENT readdir( + LPDIR dirp +){ + struct _finddata_t data; + + if( dirp==NULL ) return NULL; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + return &dirp->d_first; + } + +next: + + memset(&data, 0, sizeof(struct _finddata_t)); + if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ) goto next; + + dirp->d_next.d_ino++; + dirp->d_next.d_attributes = data.attrib; + strncpy(dirp->d_next.d_name, data.name, NAME_MAX); + dirp->d_next.d_name[NAME_MAX] = '\0'; + + return &dirp->d_next; +} + +/* +** Implementation of the POSIX readdir_r() function using the MSVCRT. +*/ +INT readdir_r( + LPDIR dirp, + LPDIRENT entry, + LPDIRENT *result +){ + struct _finddata_t data; + + if( dirp==NULL ) return EBADF; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + entry->d_ino = dirp->d_first.d_ino; + entry->d_attributes = dirp->d_first.d_attributes; + strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; + } + +next: + + memset(&data, 0, sizeof(struct _finddata_t)); + if( _findnext(dirp->d_handle, &data)==-1 ){ + *result = NULL; + return ENOENT; + } + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ) goto next; + + entry->d_ino = (ino_t)-1; /* not available */ + entry->d_attributes = data.attrib; + strncpy(entry->d_name, data.name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; +} + +/* +** Implementation of the POSIX closedir() function using the MSVCRT. +*/ +INT closedir( + LPDIR dirp +){ + INT result = 0; + + if( dirp==NULL ) return EINVAL; + + if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){ + result = _findclose(dirp->d_handle); + } + + sqlite3_free(dirp); + return result; +} + +#endif /* defined(WIN32) && defined(_MSC_VER) */ + +/************************* End test_windirent.c ********************/ +#define dirent DIRENT +#endif +/************************* Begin ../ext/misc/memtrace.c ******************/ +/* +** 2019-01-21 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements an extension that uses the SQLITE_CONFIG_MALLOC +** mechanism to add a tracing layer on top of SQLite. If this extension +** is registered prior to sqlite3_initialize(), it will cause all memory +** allocation activities to be logged on standard output, or to some other +** FILE specified by the initializer. +** +** This file needs to be compiled into the application that uses it. +** +** This extension is used to implement the --memtrace option of the +** command-line shell. +*/ +#include +#include +#include + +/* The original memory allocation routines */ +static sqlite3_mem_methods memtraceBase; +static FILE *memtraceOut; + +/* Methods that trace memory allocations */ +static void *memtraceMalloc(int n){ + if( memtraceOut ){ + fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n", + memtraceBase.xRoundup(n)); + } + return memtraceBase.xMalloc(n); +} +static void memtraceFree(void *p){ + if( p==0 ) return; + if( memtraceOut ){ + fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p)); + } + memtraceBase.xFree(p); +} +static void *memtraceRealloc(void *p, int n){ + if( p==0 ) return memtraceMalloc(n); + if( n==0 ){ + memtraceFree(p); + return 0; + } + if( memtraceOut ){ + fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n", + memtraceBase.xSize(p), memtraceBase.xRoundup(n)); + } + return memtraceBase.xRealloc(p, n); +} +static int memtraceSize(void *p){ + return memtraceBase.xSize(p); +} +static int memtraceRoundup(int n){ + return memtraceBase.xRoundup(n); +} +static int memtraceInit(void *p){ + return memtraceBase.xInit(p); +} +static void memtraceShutdown(void *p){ + memtraceBase.xShutdown(p); +} + +/* The substitute memory allocator */ +static sqlite3_mem_methods ersaztMethods = { + memtraceMalloc, + memtraceFree, + memtraceRealloc, + memtraceSize, + memtraceRoundup, + memtraceInit, + memtraceShutdown, + 0 +}; + +/* Begin tracing memory allocations to out. */ +int sqlite3MemTraceActivate(FILE *out){ + int rc = SQLITE_OK; + if( memtraceBase.xMalloc==0 ){ + rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase); + if( rc==SQLITE_OK ){ + rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods); + } + } + memtraceOut = out; + return rc; +} + +/* Deactivate memory tracing */ +int sqlite3MemTraceDeactivate(void){ + int rc = SQLITE_OK; + if( memtraceBase.xMalloc!=0 ){ + rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase); + if( rc==SQLITE_OK ){ + memset(&memtraceBase, 0, sizeof(memtraceBase)); + } + } + memtraceOut = 0; + return rc; +} + +/************************* End ../ext/misc/memtrace.c ********************/ +/************************* Begin ../ext/misc/shathree.c ******************/ +/* +** 2017-03-08 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This SQLite extension implements functions that compute SHA3 hashes. +** Two SQL functions are implemented: +** +** sha3(X,SIZE) +** sha3_query(Y,SIZE) +** +** The sha3(X) function computes the SHA3 hash of the input X, or NULL if +** X is NULL. +** +** The sha3_query(Y) function evaluates all queries in the SQL statements of Y +** and returns a hash of their results. +** +** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm +** is used. If SIZE is included it must be one of the integers 224, 256, +** 384, or 512, to determine SHA3 hash variant that is computed. +*/ +/* #include "sqlite3ext.h" */ +SQLITE_EXTENSION_INIT1 +#include +#include +#include + +#ifndef SQLITE_AMALGAMATION +/* typedef sqlite3_uint64 u64; */ +#endif /* SQLITE_AMALGAMATION */ + +/****************************************************************************** +** The Hash Engine +*/ +/* +** Macros to determine whether the machine is big or little endian, +** and whether or not that determination is run-time or compile-time. +** +** For best performance, an attempt is made to guess at the byte-order +** using C-preprocessor macros. If that is unsuccessful, or if +** -DSHA3_BYTEORDER=0 is set, then byte-order is determined +** at run-time. +*/ +#ifndef SHA3_BYTEORDER +# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ + defined(__arm__) +# define SHA3_BYTEORDER 1234 +# elif defined(sparc) || defined(__ppc__) +# define SHA3_BYTEORDER 4321 +# else +# define SHA3_BYTEORDER 0 +# endif +#endif + + +/* +** State structure for a SHA3 hash in progress +*/ +typedef struct SHA3Context SHA3Context; +struct SHA3Context { + union { + u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ + unsigned char x[1600]; /* ... or 1600 bytes */ + } u; + unsigned nRate; /* Bytes of input accepted per Keccak iteration */ + unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ + unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ +}; + +/* +** A single step of the Keccak mixing function for a 1600-bit state +*/ +static void KeccakF1600Step(SHA3Context *p){ + int i; + u64 b0, b1, b2, b3, b4; + u64 c0, c1, c2, c3, c4; + u64 d0, d1, d2, d3, d4; + static const u64 RC[] = { + 0x0000000000000001ULL, 0x0000000000008082ULL, + 0x800000000000808aULL, 0x8000000080008000ULL, + 0x000000000000808bULL, 0x0000000080000001ULL, + 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x000000000000008aULL, 0x0000000000000088ULL, + 0x0000000080008009ULL, 0x000000008000000aULL, + 0x000000008000808bULL, 0x800000000000008bULL, + 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, + 0x000000000000800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, + 0x0000000080000001ULL, 0x8000000080008008ULL + }; +# define a00 (p->u.s[0]) +# define a01 (p->u.s[1]) +# define a02 (p->u.s[2]) +# define a03 (p->u.s[3]) +# define a04 (p->u.s[4]) +# define a10 (p->u.s[5]) +# define a11 (p->u.s[6]) +# define a12 (p->u.s[7]) +# define a13 (p->u.s[8]) +# define a14 (p->u.s[9]) +# define a20 (p->u.s[10]) +# define a21 (p->u.s[11]) +# define a22 (p->u.s[12]) +# define a23 (p->u.s[13]) +# define a24 (p->u.s[14]) +# define a30 (p->u.s[15]) +# define a31 (p->u.s[16]) +# define a32 (p->u.s[17]) +# define a33 (p->u.s[18]) +# define a34 (p->u.s[19]) +# define a40 (p->u.s[20]) +# define a41 (p->u.s[21]) +# define a42 (p->u.s[22]) +# define a43 (p->u.s[23]) +# define a44 (p->u.s[24]) +# define ROL64(a,x) ((a<>(64-x))) + + for(i=0; i<24; i+=4){ + c0 = a00^a10^a20^a30^a40; + c1 = a01^a11^a21^a31^a41; + c2 = a02^a12^a22^a32^a42; + c3 = a03^a13^a23^a33^a43; + c4 = a04^a14^a24^a34^a44; + d0 = c4^ROL64(c1, 1); + d1 = c0^ROL64(c2, 1); + d2 = c1^ROL64(c3, 1); + d3 = c2^ROL64(c4, 1); + d4 = c3^ROL64(c0, 1); + + b0 = (a00^d0); + b1 = ROL64((a11^d1), 44); + b2 = ROL64((a22^d2), 43); + b3 = ROL64((a33^d3), 21); + b4 = ROL64((a44^d4), 14); + a00 = b0 ^((~b1)& b2 ); + a00 ^= RC[i]; + a11 = b1 ^((~b2)& b3 ); + a22 = b2 ^((~b3)& b4 ); + a33 = b3 ^((~b4)& b0 ); + a44 = b4 ^((~b0)& b1 ); + + b2 = ROL64((a20^d0), 3); + b3 = ROL64((a31^d1), 45); + b4 = ROL64((a42^d2), 61); + b0 = ROL64((a03^d3), 28); + b1 = ROL64((a14^d4), 20); + a20 = b0 ^((~b1)& b2 ); + a31 = b1 ^((~b2)& b3 ); + a42 = b2 ^((~b3)& b4 ); + a03 = b3 ^((~b4)& b0 ); + a14 = b4 ^((~b0)& b1 ); + + b4 = ROL64((a40^d0), 18); + b0 = ROL64((a01^d1), 1); + b1 = ROL64((a12^d2), 6); + b2 = ROL64((a23^d3), 25); + b3 = ROL64((a34^d4), 8); + a40 = b0 ^((~b1)& b2 ); + a01 = b1 ^((~b2)& b3 ); + a12 = b2 ^((~b3)& b4 ); + a23 = b3 ^((~b4)& b0 ); + a34 = b4 ^((~b0)& b1 ); + + b1 = ROL64((a10^d0), 36); + b2 = ROL64((a21^d1), 10); + b3 = ROL64((a32^d2), 15); + b4 = ROL64((a43^d3), 56); + b0 = ROL64((a04^d4), 27); + a10 = b0 ^((~b1)& b2 ); + a21 = b1 ^((~b2)& b3 ); + a32 = b2 ^((~b3)& b4 ); + a43 = b3 ^((~b4)& b0 ); + a04 = b4 ^((~b0)& b1 ); + + b3 = ROL64((a30^d0), 41); + b4 = ROL64((a41^d1), 2); + b0 = ROL64((a02^d2), 62); + b1 = ROL64((a13^d3), 55); + b2 = ROL64((a24^d4), 39); + a30 = b0 ^((~b1)& b2 ); + a41 = b1 ^((~b2)& b3 ); + a02 = b2 ^((~b3)& b4 ); + a13 = b3 ^((~b4)& b0 ); + a24 = b4 ^((~b0)& b1 ); + + c0 = a00^a20^a40^a10^a30; + c1 = a11^a31^a01^a21^a41; + c2 = a22^a42^a12^a32^a02; + c3 = a33^a03^a23^a43^a13; + c4 = a44^a14^a34^a04^a24; + d0 = c4^ROL64(c1, 1); + d1 = c0^ROL64(c2, 1); + d2 = c1^ROL64(c3, 1); + d3 = c2^ROL64(c4, 1); + d4 = c3^ROL64(c0, 1); + + b0 = (a00^d0); + b1 = ROL64((a31^d1), 44); + b2 = ROL64((a12^d2), 43); + b3 = ROL64((a43^d3), 21); + b4 = ROL64((a24^d4), 14); + a00 = b0 ^((~b1)& b2 ); + a00 ^= RC[i+1]; + a31 = b1 ^((~b2)& b3 ); + a12 = b2 ^((~b3)& b4 ); + a43 = b3 ^((~b4)& b0 ); + a24 = b4 ^((~b0)& b1 ); + + b2 = ROL64((a40^d0), 3); + b3 = ROL64((a21^d1), 45); + b4 = ROL64((a02^d2), 61); + b0 = ROL64((a33^d3), 28); + b1 = ROL64((a14^d4), 20); + a40 = b0 ^((~b1)& b2 ); + a21 = b1 ^((~b2)& b3 ); + a02 = b2 ^((~b3)& b4 ); + a33 = b3 ^((~b4)& b0 ); + a14 = b4 ^((~b0)& b1 ); + + b4 = ROL64((a30^d0), 18); + b0 = ROL64((a11^d1), 1); + b1 = ROL64((a42^d2), 6); + b2 = ROL64((a23^d3), 25); + b3 = ROL64((a04^d4), 8); + a30 = b0 ^((~b1)& b2 ); + a11 = b1 ^((~b2)& b3 ); + a42 = b2 ^((~b3)& b4 ); + a23 = b3 ^((~b4)& b0 ); + a04 = b4 ^((~b0)& b1 ); + + b1 = ROL64((a20^d0), 36); + b2 = ROL64((a01^d1), 10); + b3 = ROL64((a32^d2), 15); + b4 = ROL64((a13^d3), 56); + b0 = ROL64((a44^d4), 27); + a20 = b0 ^((~b1)& b2 ); + a01 = b1 ^((~b2)& b3 ); + a32 = b2 ^((~b3)& b4 ); + a13 = b3 ^((~b4)& b0 ); + a44 = b4 ^((~b0)& b1 ); + + b3 = ROL64((a10^d0), 41); + b4 = ROL64((a41^d1), 2); + b0 = ROL64((a22^d2), 62); + b1 = ROL64((a03^d3), 55); + b2 = ROL64((a34^d4), 39); + a10 = b0 ^((~b1)& b2 ); + a41 = b1 ^((~b2)& b3 ); + a22 = b2 ^((~b3)& b4 ); + a03 = b3 ^((~b4)& b0 ); + a34 = b4 ^((~b0)& b1 ); + + c0 = a00^a40^a30^a20^a10; + c1 = a31^a21^a11^a01^a41; + c2 = a12^a02^a42^a32^a22; + c3 = a43^a33^a23^a13^a03; + c4 = a24^a14^a04^a44^a34; + d0 = c4^ROL64(c1, 1); + d1 = c0^ROL64(c2, 1); + d2 = c1^ROL64(c3, 1); + d3 = c2^ROL64(c4, 1); + d4 = c3^ROL64(c0, 1); + + b0 = (a00^d0); + b1 = ROL64((a21^d1), 44); + b2 = ROL64((a42^d2), 43); + b3 = ROL64((a13^d3), 21); + b4 = ROL64((a34^d4), 14); + a00 = b0 ^((~b1)& b2 ); + a00 ^= RC[i+2]; + a21 = b1 ^((~b2)& b3 ); + a42 = b2 ^((~b3)& b4 ); + a13 = b3 ^((~b4)& b0 ); + a34 = b4 ^((~b0)& b1 ); + + b2 = ROL64((a30^d0), 3); + b3 = ROL64((a01^d1), 45); + b4 = ROL64((a22^d2), 61); + b0 = ROL64((a43^d3), 28); + b1 = ROL64((a14^d4), 20); + a30 = b0 ^((~b1)& b2 ); + a01 = b1 ^((~b2)& b3 ); + a22 = b2 ^((~b3)& b4 ); + a43 = b3 ^((~b4)& b0 ); + a14 = b4 ^((~b0)& b1 ); + + b4 = ROL64((a10^d0), 18); + b0 = ROL64((a31^d1), 1); + b1 = ROL64((a02^d2), 6); + b2 = ROL64((a23^d3), 25); + b3 = ROL64((a44^d4), 8); + a10 = b0 ^((~b1)& b2 ); + a31 = b1 ^((~b2)& b3 ); + a02 = b2 ^((~b3)& b4 ); + a23 = b3 ^((~b4)& b0 ); + a44 = b4 ^((~b0)& b1 ); + + b1 = ROL64((a40^d0), 36); + b2 = ROL64((a11^d1), 10); + b3 = ROL64((a32^d2), 15); + b4 = ROL64((a03^d3), 56); + b0 = ROL64((a24^d4), 27); + a40 = b0 ^((~b1)& b2 ); + a11 = b1 ^((~b2)& b3 ); + a32 = b2 ^((~b3)& b4 ); + a03 = b3 ^((~b4)& b0 ); + a24 = b4 ^((~b0)& b1 ); + + b3 = ROL64((a20^d0), 41); + b4 = ROL64((a41^d1), 2); + b0 = ROL64((a12^d2), 62); + b1 = ROL64((a33^d3), 55); + b2 = ROL64((a04^d4), 39); + a20 = b0 ^((~b1)& b2 ); + a41 = b1 ^((~b2)& b3 ); + a12 = b2 ^((~b3)& b4 ); + a33 = b3 ^((~b4)& b0 ); + a04 = b4 ^((~b0)& b1 ); + + c0 = a00^a30^a10^a40^a20; + c1 = a21^a01^a31^a11^a41; + c2 = a42^a22^a02^a32^a12; + c3 = a13^a43^a23^a03^a33; + c4 = a34^a14^a44^a24^a04; + d0 = c4^ROL64(c1, 1); + d1 = c0^ROL64(c2, 1); + d2 = c1^ROL64(c3, 1); + d3 = c2^ROL64(c4, 1); + d4 = c3^ROL64(c0, 1); + + b0 = (a00^d0); + b1 = ROL64((a01^d1), 44); + b2 = ROL64((a02^d2), 43); + b3 = ROL64((a03^d3), 21); + b4 = ROL64((a04^d4), 14); + a00 = b0 ^((~b1)& b2 ); + a00 ^= RC[i+3]; + a01 = b1 ^((~b2)& b3 ); + a02 = b2 ^((~b3)& b4 ); + a03 = b3 ^((~b4)& b0 ); + a04 = b4 ^((~b0)& b1 ); + + b2 = ROL64((a10^d0), 3); + b3 = ROL64((a11^d1), 45); + b4 = ROL64((a12^d2), 61); + b0 = ROL64((a13^d3), 28); + b1 = ROL64((a14^d4), 20); + a10 = b0 ^((~b1)& b2 ); + a11 = b1 ^((~b2)& b3 ); + a12 = b2 ^((~b3)& b4 ); + a13 = b3 ^((~b4)& b0 ); + a14 = b4 ^((~b0)& b1 ); + + b4 = ROL64((a20^d0), 18); + b0 = ROL64((a21^d1), 1); + b1 = ROL64((a22^d2), 6); + b2 = ROL64((a23^d3), 25); + b3 = ROL64((a24^d4), 8); + a20 = b0 ^((~b1)& b2 ); + a21 = b1 ^((~b2)& b3 ); + a22 = b2 ^((~b3)& b4 ); + a23 = b3 ^((~b4)& b0 ); + a24 = b4 ^((~b0)& b1 ); + + b1 = ROL64((a30^d0), 36); + b2 = ROL64((a31^d1), 10); + b3 = ROL64((a32^d2), 15); + b4 = ROL64((a33^d3), 56); + b0 = ROL64((a34^d4), 27); + a30 = b0 ^((~b1)& b2 ); + a31 = b1 ^((~b2)& b3 ); + a32 = b2 ^((~b3)& b4 ); + a33 = b3 ^((~b4)& b0 ); + a34 = b4 ^((~b0)& b1 ); + + b3 = ROL64((a40^d0), 41); + b4 = ROL64((a41^d1), 2); + b0 = ROL64((a42^d2), 62); + b1 = ROL64((a43^d3), 55); + b2 = ROL64((a44^d4), 39); + a40 = b0 ^((~b1)& b2 ); + a41 = b1 ^((~b2)& b3 ); + a42 = b2 ^((~b3)& b4 ); + a43 = b3 ^((~b4)& b0 ); + a44 = b4 ^((~b0)& b1 ); + } +} + +/* +** Initialize a new hash. iSize determines the size of the hash +** in bits and should be one of 224, 256, 384, or 512. Or iSize +** can be zero to use the default hash size of 256 bits. +*/ +static void SHA3Init(SHA3Context *p, int iSize){ + memset(p, 0, sizeof(*p)); + if( iSize>=128 && iSize<=512 ){ + p->nRate = (1600 - ((iSize + 31)&~31)*2)/8; + }else{ + p->nRate = (1600 - 2*256)/8; + } +#if SHA3_BYTEORDER==1234 + /* Known to be little-endian at compile-time. No-op */ +#elif SHA3_BYTEORDER==4321 + p->ixMask = 7; /* Big-endian */ +#else + { + static unsigned int one = 1; + if( 1==*(unsigned char*)&one ){ + /* Little endian. No byte swapping. */ + p->ixMask = 0; + }else{ + /* Big endian. Byte swap. */ + p->ixMask = 7; + } + } +#endif +} + +/* +** Make consecutive calls to the SHA3Update function to add new content +** to the hash +*/ +static void SHA3Update( + SHA3Context *p, + const unsigned char *aData, + unsigned int nData +){ + unsigned int i = 0; + if( aData==0 ) return; +#if SHA3_BYTEORDER==1234 + if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ + for(; i+7u.s[p->nLoaded/8] ^= *(u64*)&aData[i]; + p->nLoaded += 8; + if( p->nLoaded>=p->nRate ){ + KeccakF1600Step(p); + p->nLoaded = 0; + } + } + } +#endif + for(; iu.x[p->nLoaded] ^= aData[i]; +#elif SHA3_BYTEORDER==4321 + p->u.x[p->nLoaded^0x07] ^= aData[i]; +#else + p->u.x[p->nLoaded^p->ixMask] ^= aData[i]; +#endif + p->nLoaded++; + if( p->nLoaded==p->nRate ){ + KeccakF1600Step(p); + p->nLoaded = 0; + } + } +} + +/* +** After all content has been added, invoke SHA3Final() to compute +** the final hash. The function returns a pointer to the binary +** hash value. +*/ +static unsigned char *SHA3Final(SHA3Context *p){ + unsigned int i; + if( p->nLoaded==p->nRate-1 ){ + const unsigned char c1 = 0x86; + SHA3Update(p, &c1, 1); + }else{ + const unsigned char c2 = 0x06; + const unsigned char c3 = 0x80; + SHA3Update(p, &c2, 1); + p->nLoaded = p->nRate - 1; + SHA3Update(p, &c3, 1); + } + for(i=0; inRate; i++){ + p->u.x[i+p->nRate] = p->u.x[i^p->ixMask]; + } + return &p->u.x[p->nRate]; +} +/* End of the hashing logic +*****************************************************************************/ + +/* +** Implementation of the sha3(X,SIZE) function. +** +** Return a BLOB which is the SIZE-bit SHA3 hash of X. The default +** size is 256. If X is a BLOB, it is hashed as is. +** For all other non-NULL types of input, X is converted into a UTF-8 string +** and the string is hashed without the trailing 0x00 terminator. The hash +** of a NULL value is NULL. +*/ +static void sha3Func( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + SHA3Context cx; + int eType = sqlite3_value_type(argv[0]); + int nByte = sqlite3_value_bytes(argv[0]); + int iSize; + if( argc==1 ){ + iSize = 256; + }else{ + iSize = sqlite3_value_int(argv[1]); + if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ + sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " + "384 512", -1); + return; + } + } + if( eType==SQLITE_NULL ) return; + SHA3Init(&cx, iSize); + if( eType==SQLITE_BLOB ){ + SHA3Update(&cx, sqlite3_value_blob(argv[0]), nByte); + }else{ + SHA3Update(&cx, sqlite3_value_text(argv[0]), nByte); + } + sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); +} + +/* Compute a string using sqlite3_vsnprintf() with a maximum length +** of 50 bytes and add it to the hash. +*/ +static void hash_step_vformat( + SHA3Context *p, /* Add content to this context */ + const char *zFormat, + ... +){ + va_list ap; + int n; + char zBuf[50]; + va_start(ap, zFormat); + sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); + va_end(ap); + n = (int)strlen(zBuf); + SHA3Update(p, (unsigned char*)zBuf, n); +} + +/* +** Implementation of the sha3_query(SQL,SIZE) function. +** +** This function compiles and runs the SQL statement(s) given in the +** argument. The results are hashed using a SIZE-bit SHA3. The default +** size is 256. +** +** The format of the byte stream that is hashed is summarized as follows: +** +** S: +** R +** N +** I +** F +** B: +** T: +** +** is the original SQL text for each statement run and is +** the size of that text. The SQL text is UTF-8. A single R character +** occurs before the start of each row. N means a NULL value. +** I mean an 8-byte little-endian integer . F is a floating point +** number with an 8-byte little-endian IEEE floating point value . +** B means blobs of bytes. T means text rendered as +** bytes of UTF-8. The and values are expressed as an ASCII +** text integers. +** +** For each SQL statement in the X input, there is one S segment. Each +** S segment is followed by zero or more R segments, one for each row in the +** result set. After each R, there are one or more N, I, F, B, or T segments, +** one for each column in the result set. Segments are concatentated directly +** with no delimiters of any kind. +*/ +static void sha3QueryFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + const char *zSql = (const char*)sqlite3_value_text(argv[0]); + sqlite3_stmt *pStmt = 0; + int nCol; /* Number of columns in the result set */ + int i; /* Loop counter */ + int rc; + int n; + const char *z; + SHA3Context cx; + int iSize; + + if( argc==1 ){ + iSize = 256; + }else{ + iSize = sqlite3_value_int(argv[1]); + if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ + sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " + "384 512", -1); + return; + } + } + if( zSql==0 ) return; + SHA3Init(&cx, iSize); + while( zSql[0] ){ + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); + if( rc ){ + char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", + zSql, sqlite3_errmsg(db)); + sqlite3_finalize(pStmt); + sqlite3_result_error(context, zMsg, -1); + sqlite3_free(zMsg); + return; + } + if( !sqlite3_stmt_readonly(pStmt) ){ + char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); + sqlite3_finalize(pStmt); + sqlite3_result_error(context, zMsg, -1); + sqlite3_free(zMsg); + return; + } + nCol = sqlite3_column_count(pStmt); + z = sqlite3_sql(pStmt); + if( z ){ + n = (int)strlen(z); + hash_step_vformat(&cx,"S%d:",n); + SHA3Update(&cx,(unsigned char*)z,n); + } + + /* Compute a hash over the result of the query */ + while( SQLITE_ROW==sqlite3_step(pStmt) ){ + SHA3Update(&cx,(const unsigned char*)"R",1); + for(i=0; i=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'I'; + SHA3Update(&cx, x, 9); + break; + } + case SQLITE_FLOAT: { + sqlite3_uint64 u; + int j; + unsigned char x[9]; + double r = sqlite3_column_double(pStmt,i); + memcpy(&u, &r, 8); + for(j=8; j>=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'F'; + SHA3Update(&cx,x,9); + break; + } + case SQLITE_TEXT: { + int n2 = sqlite3_column_bytes(pStmt, i); + const unsigned char *z2 = sqlite3_column_text(pStmt, i); + hash_step_vformat(&cx,"T%d:",n2); + SHA3Update(&cx, z2, n2); + break; + } + case SQLITE_BLOB: { + int n2 = sqlite3_column_bytes(pStmt, i); + const unsigned char *z2 = sqlite3_column_blob(pStmt, i); + hash_step_vformat(&cx,"B%d:",n2); + SHA3Update(&cx, z2, n2); + break; + } + } + } + } + sqlite3_finalize(pStmt); + } + sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); +} + + +#ifdef _WIN32 + +#endif +int sqlite3_shathree_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + rc = sqlite3_create_function(db, "sha3", 1, + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, + 0, sha3Func, 0, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha3", 2, + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, + 0, sha3Func, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha3_query", 1, + SQLITE_UTF8 | SQLITE_DIRECTONLY, + 0, sha3QueryFunc, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha3_query", 2, + SQLITE_UTF8 | SQLITE_DIRECTONLY, + 0, sha3QueryFunc, 0, 0); + } + return rc; +} + +/************************* End ../ext/misc/shathree.c ********************/ +/************************* Begin ../ext/misc/uint.c ******************/ +/* +** 2020-04-14 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This SQLite extension implements the UINT collating sequence. +** +** UINT works like BINARY for text, except that embedded strings +** of digits compare in numeric order. +** +** * Leading zeros are handled properly, in the sense that +** they do not mess of the maginitude comparison of embedded +** strings of digits. "x00123y" is equal to "x123y". +** +** * Only unsigned integers are recognized. Plus and minus +** signs are ignored. Decimal points and exponential notation +** are ignored. +** +** * Embedded integers can be of arbitrary length. Comparison +** is *not* limited integers that can be expressed as a +** 64-bit machine integer. +*/ +/* #include "sqlite3ext.h" */ +SQLITE_EXTENSION_INIT1 +#include +#include +#include + +/* +** Compare text in lexicographic order, except strings of digits +** compare in numeric order. +*/ +static int uintCollFunc( + void *notUsed, + int nKey1, const void *pKey1, + int nKey2, const void *pKey2 +){ + const unsigned char *zA = (const unsigned char*)pKey1; + const unsigned char *zB = (const unsigned char*)pKey2; + int i=0, j=0, x; + (void)notUsed; + while( i +#include +#include +#include + +/* Mark a function parameter as unused, to suppress nuisance compiler +** warnings. */ +#ifndef UNUSED_PARAMETER +# define UNUSED_PARAMETER(X) (void)(X) +#endif + + +/* A decimal object */ +typedef struct Decimal Decimal; +struct Decimal { + char sign; /* 0 for positive, 1 for negative */ + char oom; /* True if an OOM is encountered */ + char isNull; /* True if holds a NULL rather than a number */ + char isInit; /* True upon initialization */ + int nDigit; /* Total number of digits */ + int nFrac; /* Number of digits to the right of the decimal point */ + signed char *a; /* Array of digits. Most significant first. */ +}; + +/* +** Release memory held by a Decimal, but do not free the object itself. +*/ +static void decimal_clear(Decimal *p){ + sqlite3_free(p->a); +} + +/* +** Destroy a Decimal object +*/ +static void decimal_free(Decimal *p){ + if( p ){ + decimal_clear(p); + sqlite3_free(p); + } +} + +/* +** Allocate a new Decimal object. Initialize it to the number given +** by the input string. +*/ +static Decimal *decimal_new( + sqlite3_context *pCtx, + sqlite3_value *pIn, + int nAlt, + const unsigned char *zAlt +){ + Decimal *p; + int n, i; + const unsigned char *zIn; + int iExp = 0; + p = sqlite3_malloc( sizeof(*p) ); + if( p==0 ) goto new_no_mem; + p->sign = 0; + p->oom = 0; + p->isInit = 1; + p->isNull = 0; + p->nDigit = 0; + p->nFrac = 0; + if( zAlt ){ + n = nAlt, + zIn = zAlt; + }else{ + if( sqlite3_value_type(pIn)==SQLITE_NULL ){ + p->a = 0; + p->isNull = 1; + return p; + } + n = sqlite3_value_bytes(pIn); + zIn = sqlite3_value_text(pIn); + } + p->a = sqlite3_malloc64( n+1 ); + if( p->a==0 ) goto new_no_mem; + for(i=0; isspace(zIn[i]); i++){} + if( zIn[i]=='-' ){ + p->sign = 1; + i++; + }else if( zIn[i]=='+' ){ + i++; + } + while( i='0' && c<='9' ){ + p->a[p->nDigit++] = c - '0'; + }else if( c=='.' ){ + p->nFrac = p->nDigit + 1; + }else if( c=='e' || c=='E' ){ + int j = i+1; + int neg = 0; + if( j>=n ) break; + if( zIn[j]=='-' ){ + neg = 1; + j++; + }else if( zIn[j]=='+' ){ + j++; + } + while( j='0' && zIn[j]<='9' ){ + iExp = iExp*10 + zIn[j] - '0'; + } + j++; + } + if( neg ) iExp = -iExp; + break; + } + i++; + } + if( p->nFrac ){ + p->nFrac = p->nDigit - (p->nFrac - 1); + } + if( iExp>0 ){ + if( p->nFrac>0 ){ + if( iExp<=p->nFrac ){ + p->nFrac -= iExp; + iExp = 0; + }else{ + iExp -= p->nFrac; + p->nFrac = 0; + } + } + if( iExp>0 ){ + p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 ); + if( p->a==0 ) goto new_no_mem; + memset(p->a+p->nDigit, 0, iExp); + p->nDigit += iExp; + } + }else if( iExp<0 ){ + int nExtra; + iExp = -iExp; + nExtra = p->nDigit - p->nFrac - 1; + if( nExtra ){ + if( nExtra>=iExp ){ + p->nFrac += iExp; + iExp = 0; + }else{ + iExp -= nExtra; + p->nFrac = p->nDigit - 1; + } + } + if( iExp>0 ){ + p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 ); + if( p->a==0 ) goto new_no_mem; + memmove(p->a+iExp, p->a, p->nDigit); + memset(p->a, 0, iExp); + p->nDigit += iExp; + p->nFrac += iExp; + } + } + return p; + +new_no_mem: + if( pCtx ) sqlite3_result_error_nomem(pCtx); + sqlite3_free(p); + return 0; +} + +/* +** Make the given Decimal the result. +*/ +static void decimal_result(sqlite3_context *pCtx, Decimal *p){ + char *z; + int i, j; + int n; + if( p==0 || p->oom ){ + sqlite3_result_error_nomem(pCtx); + return; + } + if( p->isNull ){ + sqlite3_result_null(pCtx); + return; + } + z = sqlite3_malloc( p->nDigit+4 ); + if( z==0 ){ + sqlite3_result_error_nomem(pCtx); + return; + } + i = 0; + if( p->nDigit==0 || (p->nDigit==1 && p->a[0]==0) ){ + p->sign = 0; + } + if( p->sign ){ + z[0] = '-'; + i = 1; + } + n = p->nDigit - p->nFrac; + if( n<=0 ){ + z[i++] = '0'; + } + j = 0; + while( n>1 && p->a[j]==0 ){ + j++; + n--; + } + while( n>0 ){ + z[i++] = p->a[j] + '0'; + j++; + n--; + } + if( p->nFrac ){ + z[i++] = '.'; + do{ + z[i++] = p->a[j] + '0'; + j++; + }while( jnDigit ); + } + z[i] = 0; + sqlite3_result_text(pCtx, z, i, sqlite3_free); +} + +/* +** SQL Function: decimal(X) +** +** Convert input X into decimal and then back into text +*/ +static void decimalFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *p = decimal_new(context, argv[0], 0, 0); + UNUSED_PARAMETER(argc); + decimal_result(context, p); + decimal_free(p); +} + +/* +** Compare to Decimal objects. Return negative, 0, or positive if the +** first object is less than, equal to, or greater than the second. +** +** Preconditions for this routine: +** +** pA!=0 +** pA->isNull==0 +** pB!=0 +** pB->isNull==0 +*/ +static int decimal_cmp(const Decimal *pA, const Decimal *pB){ + int nASig, nBSig, rc, n; + if( pA->sign!=pB->sign ){ + return pA->sign ? -1 : +1; + } + if( pA->sign ){ + const Decimal *pTemp = pA; + pA = pB; + pB = pTemp; + } + nASig = pA->nDigit - pA->nFrac; + nBSig = pB->nDigit - pB->nFrac; + if( nASig!=nBSig ){ + return nASig - nBSig; + } + n = pA->nDigit; + if( n>pB->nDigit ) n = pB->nDigit; + rc = memcmp(pA->a, pB->a, n); + if( rc==0 ){ + rc = pA->nDigit - pB->nDigit; + } + return rc; +} + +/* +** SQL Function: decimal_cmp(X, Y) +** +** Return negative, zero, or positive if X is less then, equal to, or +** greater than Y. +*/ +static void decimalCmpFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *pA = 0, *pB = 0; + int rc; + + UNUSED_PARAMETER(argc); + pA = decimal_new(context, argv[0], 0, 0); + if( pA==0 || pA->isNull ) goto cmp_done; + pB = decimal_new(context, argv[1], 0, 0); + if( pB==0 || pB->isNull ) goto cmp_done; + rc = decimal_cmp(pA, pB); + if( rc<0 ) rc = -1; + else if( rc>0 ) rc = +1; + sqlite3_result_int(context, rc); +cmp_done: + decimal_free(pA); + decimal_free(pB); +} + +/* +** Expand the Decimal so that it has a least nDigit digits and nFrac +** digits to the right of the decimal point. +*/ +static void decimal_expand(Decimal *p, int nDigit, int nFrac){ + int nAddSig; + int nAddFrac; + if( p==0 ) return; + nAddFrac = nFrac - p->nFrac; + nAddSig = (nDigit - p->nDigit) - nAddFrac; + if( nAddFrac==0 && nAddSig==0 ) return; + p->a = sqlite3_realloc64(p->a, nDigit+1); + if( p->a==0 ){ + p->oom = 1; + return; + } + if( nAddSig ){ + memmove(p->a+nAddSig, p->a, p->nDigit); + memset(p->a, 0, nAddSig); + p->nDigit += nAddSig; + } + if( nAddFrac ){ + memset(p->a+p->nDigit, 0, nAddFrac); + p->nDigit += nAddFrac; + p->nFrac += nAddFrac; + } +} + +/* +** Add the value pB into pA. +** +** Both pA and pB might become denormalized by this routine. +*/ +static void decimal_add(Decimal *pA, Decimal *pB){ + int nSig, nFrac, nDigit; + int i, rc; + if( pA==0 ){ + return; + } + if( pA->oom || pB==0 || pB->oom ){ + pA->oom = 1; + return; + } + if( pA->isNull || pB->isNull ){ + pA->isNull = 1; + return; + } + nSig = pA->nDigit - pA->nFrac; + if( nSig && pA->a[0]==0 ) nSig--; + if( nSignDigit-pB->nFrac ){ + nSig = pB->nDigit - pB->nFrac; + } + nFrac = pA->nFrac; + if( nFracnFrac ) nFrac = pB->nFrac; + nDigit = nSig + nFrac + 1; + decimal_expand(pA, nDigit, nFrac); + decimal_expand(pB, nDigit, nFrac); + if( pA->oom || pB->oom ){ + pA->oom = 1; + }else{ + if( pA->sign==pB->sign ){ + int carry = 0; + for(i=nDigit-1; i>=0; i--){ + int x = pA->a[i] + pB->a[i] + carry; + if( x>=10 ){ + carry = 1; + pA->a[i] = x - 10; + }else{ + carry = 0; + pA->a[i] = x; + } + } + }else{ + signed char *aA, *aB; + int borrow = 0; + rc = memcmp(pA->a, pB->a, nDigit); + if( rc<0 ){ + aA = pB->a; + aB = pA->a; + pA->sign = !pA->sign; + }else{ + aA = pA->a; + aB = pB->a; + } + for(i=nDigit-1; i>=0; i--){ + int x = aA[i] - aB[i] - borrow; + if( x<0 ){ + pA->a[i] = x+10; + borrow = 1; + }else{ + pA->a[i] = x; + borrow = 0; + } + } + } + } +} + +/* +** Compare text in decimal order. +*/ +static int decimalCollFunc( + void *notUsed, + int nKey1, const void *pKey1, + int nKey2, const void *pKey2 +){ + const unsigned char *zA = (const unsigned char*)pKey1; + const unsigned char *zB = (const unsigned char*)pKey2; + Decimal *pA = decimal_new(0, 0, nKey1, zA); + Decimal *pB = decimal_new(0, 0, nKey2, zB); + int rc; + UNUSED_PARAMETER(notUsed); + if( pA==0 || pB==0 ){ + rc = 0; + }else{ + rc = decimal_cmp(pA, pB); + } + decimal_free(pA); + decimal_free(pB); + return rc; +} + + +/* +** SQL Function: decimal_add(X, Y) +** decimal_sub(X, Y) +** +** Return the sum or difference of X and Y. +*/ +static void decimalAddFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *pA = decimal_new(context, argv[0], 0, 0); + Decimal *pB = decimal_new(context, argv[1], 0, 0); + UNUSED_PARAMETER(argc); + decimal_add(pA, pB); + decimal_result(context, pA); + decimal_free(pA); + decimal_free(pB); +} +static void decimalSubFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *pA = decimal_new(context, argv[0], 0, 0); + Decimal *pB = decimal_new(context, argv[1], 0, 0); + UNUSED_PARAMETER(argc); + if( pB ){ + pB->sign = !pB->sign; + decimal_add(pA, pB); + decimal_result(context, pA); + } + decimal_free(pA); + decimal_free(pB); +} + +/* Aggregate funcion: decimal_sum(X) +** +** Works like sum() except that it uses decimal arithmetic for unlimited +** precision. +*/ +static void decimalSumStep( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *p; + Decimal *pArg; + UNUSED_PARAMETER(argc); + p = sqlite3_aggregate_context(context, sizeof(*p)); + if( p==0 ) return; + if( !p->isInit ){ + p->isInit = 1; + p->a = sqlite3_malloc(2); + if( p->a==0 ){ + p->oom = 1; + }else{ + p->a[0] = 0; + } + p->nDigit = 1; + p->nFrac = 0; + } + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; + pArg = decimal_new(context, argv[0], 0, 0); + decimal_add(p, pArg); + decimal_free(pArg); +} +static void decimalSumInverse( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *p; + Decimal *pArg; + UNUSED_PARAMETER(argc); + p = sqlite3_aggregate_context(context, sizeof(*p)); + if( p==0 ) return; + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; + pArg = decimal_new(context, argv[0], 0, 0); + if( pArg ) pArg->sign = !pArg->sign; + decimal_add(p, pArg); + decimal_free(pArg); +} +static void decimalSumValue(sqlite3_context *context){ + Decimal *p = sqlite3_aggregate_context(context, 0); + if( p==0 ) return; + decimal_result(context, p); +} +static void decimalSumFinalize(sqlite3_context *context){ + Decimal *p = sqlite3_aggregate_context(context, 0); + if( p==0 ) return; + decimal_result(context, p); + decimal_clear(p); +} + +/* +** SQL Function: decimal_mul(X, Y) +** +** Return the product of X and Y. +** +** All significant digits after the decimal point are retained. +** Trailing zeros after the decimal point are omitted as long as +** the number of digits after the decimal point is no less than +** either the number of digits in either input. +*/ +static void decimalMulFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *pA = decimal_new(context, argv[0], 0, 0); + Decimal *pB = decimal_new(context, argv[1], 0, 0); + signed char *acc = 0; + int i, j, k; + int minFrac; + UNUSED_PARAMETER(argc); + if( pA==0 || pA->oom || pA->isNull + || pB==0 || pB->oom || pB->isNull + ){ + goto mul_end; + } + acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 ); + if( acc==0 ){ + sqlite3_result_error_nomem(context); + goto mul_end; + } + memset(acc, 0, pA->nDigit + pB->nDigit + 2); + minFrac = pA->nFrac; + if( pB->nFracnFrac; + for(i=pA->nDigit-1; i>=0; i--){ + signed char f = pA->a[i]; + int carry = 0, x; + for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){ + x = acc[k] + f*pB->a[j] + carry; + acc[k] = x%10; + carry = x/10; + } + x = acc[k] + carry; + acc[k] = x%10; + acc[k-1] += x/10; + } + sqlite3_free(pA->a); + pA->a = acc; + acc = 0; + pA->nDigit += pB->nDigit + 2; + pA->nFrac += pB->nFrac; + pA->sign ^= pB->sign; + while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){ + pA->nFrac--; + pA->nDigit--; + } + decimal_result(context, pA); + +mul_end: + sqlite3_free(acc); + decimal_free(pA); + decimal_free(pB); +} + +#ifdef _WIN32 + +#endif +int sqlite3_decimal_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + static const struct { + const char *zFuncName; + int nArg; + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); + } aFunc[] = { + { "decimal", 1, decimalFunc }, + { "decimal_cmp", 2, decimalCmpFunc }, + { "decimal_add", 2, decimalAddFunc }, + { "decimal_sub", 2, decimalSubFunc }, + { "decimal_mul", 2, decimalMulFunc }, + }; + unsigned int i; + (void)pzErrMsg; /* Unused parameter */ + + SQLITE_EXTENSION_INIT2(pApi); + + for(i=0; i 'ieee754(2,0)' +** ieee754(45.25) -> 'ieee754(181,-2)' +** ieee754(2, 0) -> 2.0 +** ieee754(181, -2) -> 45.25 +** +** Two additional functions break apart the one-argument ieee754() +** result into separate integer values: +** +** ieee754_mantissa(45.25) -> 181 +** ieee754_exponent(45.25) -> -2 +** +** These functions convert binary64 numbers into blobs and back again. +** +** ieee754_from_blob(x'3ff0000000000000') -> 1.0 +** ieee754_to_blob(1.0) -> x'3ff0000000000000' +** +** In all single-argument functions, if the argument is an 8-byte blob +** then that blob is interpreted as a big-endian binary64 value. +** +** +** EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES +** ----------------------------------------------- +** +** This extension in combination with the separate 'decimal' extension +** can be used to compute the exact decimal representation of binary64 +** values. To begin, first compute a table of exponent values: +** +** CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT); +** WITH RECURSIVE c(x,v) AS ( +** VALUES(0,'1') +** UNION ALL +** SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971 +** ) INSERT INTO pow2(x,v) SELECT x, v FROM c; +** WITH RECURSIVE c(x,v) AS ( +** VALUES(-1,'0.5') +** UNION ALL +** SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075 +** ) INSERT INTO pow2(x,v) SELECT x, v FROM c; +** +** Then, to compute the exact decimal representation of a floating +** point value (the value 47.49 is used in the example) do: +** +** WITH c(n) AS (VALUES(47.49)) +** ---------------^^^^^---- Replace with whatever you want +** SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v) +** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n); +** +** Here is a query to show various boundry values for the binary64 +** number format: +** +** WITH c(name,bin) AS (VALUES +** ('minimum positive value', x'0000000000000001'), +** ('maximum subnormal value', x'000fffffffffffff'), +** ('mininum positive nornal value', x'0010000000000000'), +** ('maximum value', x'7fefffffffffffff')) +** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v) +** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin); +** +*/ +/* #include "sqlite3ext.h" */ +SQLITE_EXTENSION_INIT1 +#include +#include + +/* Mark a function parameter as unused, to suppress nuisance compiler +** warnings. */ +#ifndef UNUSED_PARAMETER +# define UNUSED_PARAMETER(X) (void)(X) +#endif + +/* +** Implementation of the ieee754() function +*/ +static void ieee754func( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + if( argc==1 ){ + sqlite3_int64 m, a; + double r; + int e; + int isNeg; + char zResult[100]; + assert( sizeof(m)==sizeof(r) ); + if( sqlite3_value_type(argv[0])==SQLITE_BLOB + && sqlite3_value_bytes(argv[0])==sizeof(r) + ){ + const unsigned char *x = sqlite3_value_blob(argv[0]); + unsigned int i; + sqlite3_uint64 v = 0; + for(i=0; i>52; + m = a & ((((sqlite3_int64)1)<<52)-1); + if( e==0 ){ + m <<= 1; + }else{ + m |= ((sqlite3_int64)1)<<52; + } + while( e<1075 && m>0 && (m&1)==0 ){ + m >>= 1; + e++; + } + if( isNeg ) m = -m; + } + switch( *(int*)sqlite3_user_data(context) ){ + case 0: + sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)", + m, e-1075); + sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT); + break; + case 1: + sqlite3_result_int64(context, m); + break; + case 2: + sqlite3_result_int(context, e-1075); + break; + } + }else{ + sqlite3_int64 m, e, a; + double r; + int isNeg = 0; + m = sqlite3_value_int64(argv[0]); + e = sqlite3_value_int64(argv[1]); + + /* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */ + if( e>10000 ){ + e = 10000; + }else if( e<-10000 ){ + e = -10000; + } + + if( m<0 ){ + isNeg = 1; + m = -m; + if( m<0 ) return; + }else if( m==0 && e>-1000 && e<1000 ){ + sqlite3_result_double(context, 0.0); + return; + } + while( (m>>32)&0xffe00000 ){ + m >>= 1; + e++; + } + while( m!=0 && ((m>>32)&0xfff00000)==0 ){ + m <<= 1; + e--; + } + e += 1075; + if( e<=0 ){ + /* Subnormal */ + if( 1-e >= 64 ){ + m = 0; + }else{ + m >>= 1-e; + } + e = 0; + }else if( e>0x7ff ){ + e = 0x7ff; + } + a = m & ((((sqlite3_int64)1)<<52)-1); + a |= e<<52; + if( isNeg ) a |= ((sqlite3_uint64)1)<<63; + memcpy(&r, &a, sizeof(r)); + sqlite3_result_double(context, r); + } +} + +/* +** Functions to convert between blobs and floats. +*/ +static void ieee754func_from_blob( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + UNUSED_PARAMETER(argc); + if( sqlite3_value_type(argv[0])==SQLITE_BLOB + && sqlite3_value_bytes(argv[0])==sizeof(double) + ){ + double r; + const unsigned char *x = sqlite3_value_blob(argv[0]); + unsigned int i; + sqlite3_uint64 v = 0; + for(i=0; i>= 8; + } + sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT); + } +} + + +#ifdef _WIN32 + +#endif +int sqlite3_ieee_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + static const struct { + char *zFName; + int nArg; + int iAux; + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); + } aFunc[] = { + { "ieee754", 1, 0, ieee754func }, + { "ieee754", 2, 0, ieee754func }, + { "ieee754_mantissa", 1, 1, ieee754func }, + { "ieee754_exponent", 1, 2, ieee754func }, + { "ieee754_to_blob", 1, 0, ieee754func_to_blob }, + { "ieee754_from_blob", 1, 0, ieee754func_from_blob }, + + }; + unsigned int i; + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + for(i=0; i