2019-10-23 21:00:40 +00:00
/*
* * 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 - 9 C0B - 4 EF8 - BB6D - 6 BB9BD380A11
* * { a0eebc99 - 9 c0b - 4 ef8 - bb6d - 6 bb9bd380a11 }
* * a0eebc999c0b4ef8bb6d6bb9bd380a11
* * a0ee - bc99 - 9 c0b - 4 ef8 - bb6d - 6 bb9 - bd38 - 0 a11
* * { a0eebc99 - 9 c0b4ef8 - 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 - 9 c0b - 4 ef8 - bb6d - 6 bb9bd380a11
* *
* * 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 <assert.h>
# include <string.h>
# include <ctype.h>
# 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. .9 a . . 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 */
2020-01-07 19:45:40 +00:00
rc = sqlite3_create_function ( db , " uuid " , 0 , SQLITE_UTF8 | SQLITE_INNOCUOUS , 0 ,
2019-10-23 21:00:40 +00:00
sqlite3UuidFunc , 0 , 0 ) ;
if ( rc = = SQLITE_OK ) {
2020-01-07 19:45:40 +00:00
rc = sqlite3_create_function ( db , " uuid_str " , 1 ,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC ,
0 , sqlite3UuidStrFunc , 0 , 0 ) ;
2019-10-23 21:00:40 +00:00
}
if ( rc = = SQLITE_OK ) {
2020-01-07 19:45:40 +00:00
rc = sqlite3_create_function ( db , " uuid_blob " , 1 ,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC ,
0 , sqlite3UuidBlobFunc , 0 , 0 ) ;
2019-10-23 21:00:40 +00:00
}
return rc ;
}