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-17 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)
24 {
25     import std.conv : to;
26     return sqlite3_errmsg(db).to!string;
27 }
28 
29 string errmsg(sqlite3_stmt* stmt)
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()
48         {
49             return !sql.length;
50         }
51 
52         string front()
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 
69             size_t pos;
70             bool complete;
71             do
72             {
73                 auto tail = sql[pos .. $];
74                 immutable offset = tail.countUntil(';') + 1;
75                 pos += offset;
76                 if (offset == 0)
77                     pos = sql.length;
78                 auto part = sql[0 .. pos];
79                 complete = cast(bool) sqlite3_complete(part.toStringz);
80             }
81             while (!complete && pos < sql.length);
82             return pos;
83         }
84     }
85 
86     return ByStatement(sql);
87 }
88 unittest
89 {
90     import std.algorithm : equal, map;
91     import std..string : strip;
92 
93     auto sql = "CREATE TABLE test (dummy);
94         CREATE TRIGGER trig INSERT ON test BEGIN SELECT 1; SELECT 'a;b'; END;
95         SELECT 'c;d';;
96         CREATE";
97     assert(equal(sql.byStatement.map!(s => s.strip), [
98         "CREATE TABLE test (dummy);",
99         "CREATE TRIGGER trig INSERT ON test BEGIN SELECT 1; SELECT 'a;b'; END;",
100         "SELECT 'c;d';",
101         ";",
102         "CREATE"
103     ]));
104 }
105 
106 // getValue and setResult function templates
107 // used by createFunction and createAggregate
108 
109 auto getValue(T)(sqlite3_value* argv)
110     if (isBoolean!T)
111 {
112     return sqlite3_value_int64(argv) != 0;
113 }
114 
115 auto getValue(T)(sqlite3_value* argv)
116     if (isIntegral!T)
117 {
118     import std.conv : to;
119     return sqlite3_value_int64(argv).to!T;
120 }
121 
122 auto getValue(T)(sqlite3_value* argv)
123     if (isFloatingPoint!T)
124 {
125     import std.conv : to;
126     if (sqlite3_value_type(argv) == SQLITE_NULL)
127         return double.nan;
128     return sqlite3_value_double(argv).to!T;
129 }
130 
131 auto getValue(T)(sqlite3_value* argv)
132     if (isSomeString!T)
133 {
134     import std.conv : to;
135     return (cast(const(char)*) sqlite3_value_text(argv)).to!T;
136 }
137 
138 auto getValue(T)(sqlite3_value* argv)
139     if (isArray!T && !isSomeString!T)
140 {
141     import std.conv : to;
142     import core.stdc..string : memcpy;
143 
144     auto n = sqlite3_value_bytes(argv);
145     ubyte[] blob;
146     blob.length = n;
147     memcpy(blob.ptr, sqlite3_value_blob(argv), n);
148     return cast(T) blob;
149 }
150 
151 auto getValue(T : Nullable!U, U...)(sqlite3_value* argv)
152 {
153     if (sqlite3_value_type(argv) == SQLITE_NULL)
154         return T.init;
155     return T(getValue!(U[0])(argv));
156 }
157 
158 void setResult(T)(sqlite3_context* context, T value)
159     if (isIntegral!T || isBoolean!T)
160 {
161     import std.conv : to;
162     sqlite3_result_int64(context, value.to!long);
163 }
164 
165 void setResult(T)(sqlite3_context* context, T value)
166     if (isFloatingPoint!T)
167 {
168     import std.conv : to;
169     sqlite3_result_double(context, value.to!double);
170 }
171 
172 void setResult(T)(sqlite3_context* context, T value)
173     if (isSomeString!T)
174 {
175     import std.conv : to;
176     auto val = value.to!string;
177     sqlite3_result_text64(context, cast(const(char)*) anchorMem(cast(void*) val.ptr),
178         val.length, &releaseMem, SQLITE_UTF8);
179 }
180 
181 void setResult(T)(sqlite3_context* context, T value)
182     if (isDynamicArray!T && !isSomeString!T)
183 {
184     auto val = cast(void[]) value;
185     sqlite3_result_blob64(context, anchorMem(val.ptr), val.length, &releaseMem);
186 }
187 
188 void setResult(T)(sqlite3_context* context, T value)
189     if (isStaticArray!T)
190 {
191     auto val = cast(void[]) value;
192     sqlite3_result_blob64(context, val.ptr, val.sizeof, SQLITE_TRANSIENT);
193 }
194 
195 void setResult(T : Nullable!U, U...)(sqlite3_context* context, T value)
196 {
197     if (value.isNull)
198         sqlite3_result_null(context);
199     else
200         setResult(context, value.get);
201 }
202 
203 string nothrowFormat(Args...)(string fmt, Args args) nothrow
204 {
205     import std..string : format;
206     try
207         return fmt.format(args);
208     catch (Exception e)
209         throw new Error("Error: " ~ e.msg);
210 }