1 /+
2 This module is part of d2sqlite3.
3 
4 Authors:
5     Nicolas Sicard (biozic) and other contributors at $(LINK https://github.com/biozic/d2sqlite3)
6 
7 Copyright:
8     Copyright 2011-18 Nicolas Sicard.
9 
10 License:
11     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
12 +/
13 module d2sqlite3.internal.util;
14 
15 import std.traits : isBoolean, isIntegral, isFloatingPoint, isSomeString,
16     isArray, isStaticArray, isDynamicArray;
17 import std.typecons : Nullable;
18 import d2sqlite3.sqlite3;
19 import d2sqlite3.internal.memory;
20 
21 package(d2sqlite3):
22 
23 string errmsg(sqlite3* db) nothrow
24 {
25     import std.conv : to;
26     return sqlite3_errmsg(db).to!string;
27 }
28 
29 string errmsg(sqlite3_stmt* stmt) nothrow
30 {
31     return errmsg(sqlite3_db_handle(stmt));
32 }
33 
34 auto byStatement(string sql)
35 {
36     static struct ByStatement
37     {
38         string sql;
39         size_t end;
40 
41         this(string sql)
42         {
43             this.sql = sql;
44             end = findEnd();
45         }
46 
47         bool empty() const @safe pure nothrow @nogc
48         {
49             return !sql.length;
50         }
51 
52         string front() const @safe pure nothrow @nogc
53         {
54             return sql[0 .. end];
55         }
56 
57         void popFront()
58         {
59             sql = sql[end .. $];
60             end = findEnd();
61         }
62 
63     private:
64         size_t findEnd()
65         {
66             import std.algorithm : countUntil;
67             import std..string : toStringz;
68             import std.utf : byCodeUnit;
69 
70             size_t pos;
71             bool complete;
72             do
73             {
74                 auto tail = sql[pos .. $];
75                 immutable offset = tail.byCodeUnit.countUntil(';') + 1;
76                 pos += offset;
77                 if (offset == 0)
78                     pos = sql.length;
79                 auto part = sql[0 .. pos];
80                 complete = cast(bool) sqlite3_complete(part.toStringz);
81             }
82             while (!complete && pos < sql.length);
83             return pos;
84         }
85     }
86 
87     return ByStatement(sql);
88 }
89 unittest
90 {
91     import std.algorithm : equal, map;
92     import std..string : strip;
93 
94     auto sql = "CREATE TABLE test (dummy);
95         CREATE TRIGGER trig INSERT ON test BEGIN SELECT 1; SELECT 'a;b'; END;
96         SELECT 'c;d';;
97         CREATE";
98     assert(equal(sql.byStatement.map!(s => s.strip), [
99         "CREATE TABLE test (dummy);",
100         "CREATE TRIGGER trig INSERT ON test BEGIN SELECT 1; SELECT 'a;b'; END;",
101         "SELECT 'c;d';",
102         ";",
103         "CREATE"
104     ]));
105 }
106 
107 // getValue and setResult function templates
108 // used by createFunction and createAggregate
109 
110 auto getValue(T)(sqlite3_value* argv)
111     if (isBoolean!T)
112 {
113     return sqlite3_value_int64(argv) != 0;
114 }
115 
116 auto getValue(T)(sqlite3_value* argv)
117     if (isIntegral!T)
118 {
119     import std.conv : to;
120     return sqlite3_value_int64(argv).to!T;
121 }
122 
123 auto getValue(T)(sqlite3_value* argv)
124     if (isFloatingPoint!T)
125 {
126     import std.conv : to;
127     if (sqlite3_value_type(argv) == SQLITE_NULL)
128         return double.nan;
129     return sqlite3_value_double(argv).to!T;
130 }
131 
132 auto getValue(T)(sqlite3_value* argv)
133     if (isSomeString!T)
134 {
135     import std.conv : to;
136     return (cast(const(char)*) sqlite3_value_text(argv)).to!T;
137 }
138 
139 auto getValue(T)(sqlite3_value* argv)
140     if (isArray!T && !isSomeString!T)
141 {
142     import std.conv : to;
143     import core.stdc..string : memcpy;
144 
145     auto n = sqlite3_value_bytes(argv);
146     ubyte[] blob;
147     blob.length = n;
148     memcpy(blob.ptr, sqlite3_value_blob(argv), n);
149     return cast(T) blob;
150 }
151 
152 auto getValue(T : Nullable!U, U...)(sqlite3_value* argv)
153 {
154     if (sqlite3_value_type(argv) == SQLITE_NULL)
155         return T.init;
156     return T(getValue!(U[0])(argv));
157 }
158 
159 void setResult(T)(sqlite3_context* context, T value)
160     if (isIntegral!T || isBoolean!T)
161 {
162     import std.conv : to;
163     sqlite3_result_int64(context, value.to!long);
164 }
165 
166 void setResult(T)(sqlite3_context* context, T value)
167     if (isFloatingPoint!T)
168 {
169     import std.conv : to;
170     sqlite3_result_double(context, value.to!double);
171 }
172 
173 void setResult(T)(sqlite3_context* context, T value)
174     if (isSomeString!T)
175 {
176     import std.conv : to;
177     auto val = value.to!string;
178     sqlite3_result_text64(context, cast(const(char)*) anchorMem(cast(void*) val.ptr),
179         val.length, &releaseMem, SQLITE_UTF8);
180 }
181 
182 void setResult(T)(sqlite3_context* context, T value)
183     if (isDynamicArray!T && !isSomeString!T)
184 {
185     auto val = cast(void[]) value;
186     sqlite3_result_blob64(context, anchorMem(val.ptr), val.length, &releaseMem);
187 }
188 
189 void setResult(T)(sqlite3_context* context, T value)
190     if (isStaticArray!T)
191 {
192     auto val = cast(void[]) value;
193     sqlite3_result_blob64(context, val.ptr, val.sizeof, SQLITE_TRANSIENT);
194 }
195 
196 void setResult(T : Nullable!U, U...)(sqlite3_context* context, T value)
197 {
198     if (value.isNull)
199         sqlite3_result_null(context);
200     else
201         setResult(context, value.get);
202 }
203 
204 string nothrowFormat(Args...)(string fmt, Args args) nothrow
205 {
206     import std..string : format;
207     try
208         return fmt.format(args);
209     catch (Exception e)
210         throw new Error("Error: " ~ e.msg);
211 }