]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add an experimental set of UUID processing functions as the uuid.c
authordrh <drh@noemail.net>
Wed, 23 Oct 2019 21:00:40 +0000 (21:00 +0000)
committerdrh <drh@noemail.net>
Wed, 23 Oct 2019 21:00:40 +0000 (21:00 +0000)
extension in ext/misc/uuid.c.

FossilOrigin-Name: 1a6e009372cf9571e540812c8607e95a86740112a1091f03e3f632339eccb629

ext/misc/uuid.c [new file with mode: 0644]
manifest
manifest.uuid

diff --git a/ext/misc/uuid.c b/ext/misc/uuid.c
new file mode 100644 (file)
index 0000000..cb2878b
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+** 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 <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..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, 0,
+                               sqlite3UuidFunc, 0, 0);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_function(db, "uuid_str", 1, SQLITE_UTF8, 0,
+                                 sqlite3UuidStrFunc, 0, 0);
+  }
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_function(db, "uuid_blob", 1, SQLITE_UTF8, 0,
+                                 sqlite3UuidBlobFunc, 0, 0);
+  }
+  return rc;
+}
index 589ff26abd2eade9561b576a5edf06aab565f808..b48aecb39e3ab6f2dbba60f0cfa6b2578b47973b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C When\sa\svector\scomparison\sappears\sin\sthe\sWHERE\sclause\sand\sthe\sconstraint\sside\nhas\sa\sCOLLATE\sclause\son\sthe\sfirst\sterm\sof\sthe\svector,\sbe\ssure\sto\shonor\sthat\nCOLLATE\sclause.\s\sTicket\s[135c9da7513e5a97].
-D 2019-10-23T18:09:39.550
+C Add\san\sexperimental\sset\sof\sUUID\sprocessing\sfunctions\sas\sthe\suuid.c\nextension\sin\sext/misc/uuid.c.
+D 2019-10-23T21:00:40.803
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -318,6 +318,7 @@ F ext/misc/stmt.c 8a8dc4675042e4551e4afe99b8d0cc7a4a2fc1a8dacc0a9ce1b1bbff145da9
 F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
 F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
 F ext/misc/unionvtab.c 36237f0607ca954ac13a4a0e2d2ac40c33bc6e032a5f55f431713061ef1625f9
+F ext/misc/uuid.c db4db81e8c6a92ad6176ebd9f81dcb6870e331e1a286d0452f4319e3ba3df812
 F ext/misc/vfslog.c 3b25c2f56ba60788db247287be6ab024b53c4afffd412b4876db563389be0d35
 F ext/misc/vfsstat.c 77b5b4235c9f7f11eddf82487c0a422944ac2f132dafd5af3be7a68a057b1cdb
 F ext/misc/vtablog.c 5538acd0c8ddaae372331bee11608d76973436b77d6a91e8635cfc9432fba5ae
@@ -1847,7 +1848,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 5c118617cf08e17a6edfdfba86e3fc49132a780990b68b52724c2aaeac85f506
-R 24923192bf7ab8d42b7ba3bf0a0f6b7d
+P 978b2d20cf95d0b7143e3104ce1e9d5c85002867b554dc6b21deb528b730bbc7
+R 64938473a0a5ad3e464c05ab968a1d3f
 U drh
-Z 19f14a6ea97ac0c0f4e66fe45a2ec552
+Z 064b470a12983289ba0d29967199891d
index dc947ef36d68cebca851648739afa315b16ad378..c7965852a7834dcb4ce98f109e400e6e4bbde8e7 100644 (file)
@@ -1 +1 @@
-978b2d20cf95d0b7143e3104ce1e9d5c85002867b554dc6b21deb528b730bbc7
\ No newline at end of file
+1a6e009372cf9571e540812c8607e95a86740112a1091f03e3f632339eccb629
\ No newline at end of file