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