sqlite_extensions-uuid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,233 @@
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,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SqliteExtensions
4
+ module UUID
5
+ def self.to_path
6
+ spec = Gem.loaded_specs["sqlite_extensions-uuid"]
7
+ File.join(spec.require_path, "sqlite_extensions/uuid/uuid")
8
+ end
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sqlite_extensions-uuid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mark Delk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-12-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sqlite3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: debug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake-compiler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - jethrodaniel@gmail.com
100
+ executables: []
101
+ extensions:
102
+ - ext/sqlite_extensions/uuid/extconf.rb
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ext/sqlite_extensions/uuid/extconf.rb
106
+ - ext/sqlite_extensions/uuid/sqlite3.h
107
+ - ext/sqlite_extensions/uuid/sqlite3ext.h
108
+ - ext/sqlite_extensions/uuid/uuid.c
109
+ - lib/sqlite_extensions/uuid.rb
110
+ homepage: https://github.com/jethrodaniel/sqlite_extensions-uuid
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 3.0.0
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubygems_version: 3.5.21
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: SQLite's UUID v4 extension, packaged as a gem
133
+ test_files: []