/* ** 2019-10-23 ** ** 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 handling RFC-4122 UUIDs ** Three SQL functions are implemented: ** ** uuid() - generate a version 4 UUID as a string ** uuid_str(X) - convert a UUID X into a well-formed UUID string ** uuid_blob(X) - convert a UUID X into a 16-byte blob ** ** The output from uuid() and uuid_str(X) are always well-formed RFC-4122 ** UUID strings in this format: ** ** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx ** ** All of the 'x', 'M', and 'N' values are lower-case hexadecimal digits. ** The M digit indicates the "version". For uuid()-generated UUIDs, the ** version is always "4" (a random UUID). The upper three bits of N digit ** are the "variant". This library only supports variant 1 (indicated ** by values of N between '8' and 'b') as those are overwhelming the most ** common. Other variants are for legacy compatibility only. ** ** The output of uuid_blob(X) is always a 16-byte blob. The UUID input ** string is converted in network byte order (big-endian) in accordance ** with RFC-4122 specifications for variant-1 UUIDs. Note that network ** byte order is *always* used, even if the input self-identifies as a ** variant-2 UUID. ** ** The input X to the uuid_str() and uuid_blob() functions can be either ** a string or a BLOB. If it is a BLOB it must be exactly 16 bytes in ** length or else a NULL is returned. If the input is a string it must ** consist of 32 hexadecimal digits, upper or lower case, optionally ** surrounded by {...} and with optional "-" characters interposed in the ** middle. The flexibility of input is inspired by the PostgreSQL ** implementation of UUID functions that accept in all of the following ** formats: ** ** A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11 ** {a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11} ** a0eebc999c0b4ef8bb6d6bb9bd380a11 ** a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11 ** {a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11} ** ** If any of the above inputs are passed into uuid_str(), the output will ** always be in the canonical RFC-4122 format: ** ** a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 ** ** If the X input string has too few or too many digits or contains ** stray characters other than {, }, or -, then NULL is returned. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include #include #include #if !defined(SQLITE_ASCII) && !defined(SQLITE_EBCDIC) #define SQLITE_ASCII 1 #endif /* ** Translate a single byte of Hex into an integer. ** This routine only works if h really is a valid hexadecimal ** character: 0..9a..fA..F */ static unsigned char sqlite3UuidHexToInt(int h) { assert((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') || (h >= 'A' && h <= 'F')); #ifdef SQLITE_ASCII h += 9 * (1 & (h >> 6)); #endif #ifdef SQLITE_EBCDIC h += 9 * (1 & ~(h >> 4)); #endif return (unsigned char)(h & 0xf); } /* ** Convert a 16-byte BLOB into a well-formed RFC-4122 UUID. The output ** buffer zStr should be at least 37 bytes in length. The output will ** be zero-terminated. */ static void sqlite3UuidBlobToStr(const unsigned char *aBlob, /* Input blob */ unsigned char *zStr /* Write the answer here */ ) { static const char zDigits[] = "0123456789abcdef"; int i, k; unsigned char x; k = 0; for (i = 0, k = 0x550; i < 16; i++, k = k >> 1) { if (k & 1) { zStr[0] = '-'; zStr++; } x = aBlob[i]; zStr[0] = zDigits[x >> 4]; zStr[1] = zDigits[x & 0xf]; zStr += 2; } *zStr = 0; } /* ** Attempt to parse a zero-terminated input string zStr into a binary ** UUID. Return 0 on success, or non-zero if the input string is not ** parsable. */ static int sqlite3UuidStrToBlob(const unsigned char *zStr, /* Input string */ unsigned char *aBlob /* Write results here */ ) { int i; if (zStr[0] == '{') zStr++; for (i = 0; i < 16; i++) { if (zStr[0] == '-') zStr++; if (isxdigit(zStr[0]) && isxdigit(zStr[1])) { aBlob[i] = (sqlite3UuidHexToInt(zStr[0]) << 4) + sqlite3UuidHexToInt(zStr[1]); zStr += 2; } else { return 1; } } if (zStr[0] == '}') zStr++; return zStr[0] != 0; } /* ** Render sqlite3_value pIn as a 16-byte UUID blob. Return a pointer ** to the blob, or NULL if the input is not well-formed. */ static const unsigned char * sqlite3UuidInputToBlob(sqlite3_value *pIn, /* Input text */ unsigned char *pBuf /* output buffer */ ) { switch (sqlite3_value_type(pIn)) { case SQLITE_TEXT: { const unsigned char *z = sqlite3_value_text(pIn); if (sqlite3UuidStrToBlob(z, pBuf)) return 0; return pBuf; } case SQLITE_BLOB: { int n = sqlite3_value_bytes(pIn); return n == 16 ? sqlite3_value_blob(pIn) : 0; } default: { return 0; } } } /* Implementation of uuid() */ static void sqlite3UuidFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { unsigned char aBlob[16]; unsigned char zStr[37]; (void)argc; (void)argv; sqlite3_randomness(16, aBlob); aBlob[6] = (aBlob[6] & 0x0f) + 0x40; aBlob[8] = (aBlob[8] & 0x3f) + 0x80; sqlite3UuidBlobToStr(aBlob, zStr); sqlite3_result_text(context, (char *)zStr, 36, SQLITE_TRANSIENT); } /* Implementation of uuid_str() */ static void sqlite3UuidStrFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { unsigned char aBlob[16]; unsigned char zStr[37]; const unsigned char *pBlob; (void)argc; pBlob = sqlite3UuidInputToBlob(argv[0], aBlob); if (pBlob == 0) return; sqlite3UuidBlobToStr(pBlob, zStr); sqlite3_result_text(context, (char *)zStr, 36, SQLITE_TRANSIENT); } /* Implementation of uuid_blob() */ static void sqlite3UuidBlobFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { unsigned char aBlob[16]; const unsigned char *pBlob; (void)argc; pBlob = sqlite3UuidInputToBlob(argv[0], aBlob); if (pBlob == 0) return; sqlite3_result_blob(context, pBlob, 16, SQLITE_TRANSIENT); } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_uuid_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, "uuid", 0, SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, sqlite3UuidFunc, 0, 0); if (rc == SQLITE_OK) { rc = sqlite3_create_function(db, "uuid_str", 1, SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, 0, sqlite3UuidStrFunc, 0, 0); } if (rc == SQLITE_OK) { rc = sqlite3_create_function(db, "uuid_blob", 1, SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, 0, sqlite3UuidBlobFunc, 0, 0); } return rc; }