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) {
24     import std.conv : to;
25 
26     return sqlite3_errmsg(db).to!string;
27 }
28 
29 string errmsg(sqlite3_stmt* stmt) {
30     return errmsg(sqlite3_db_handle(stmt));
31 }
32 
33 auto byStatement(string sql) {
34     static struct ByStatement {
35         string sql;
36         size_t end;
37 
38         this(string sql) {
39             this.sql = sql;
40             end = findEnd();
41         }
42 
43         bool empty() {
44             return !sql.length;
45         }
46 
47         string front() {
48             return sql[0 .. end];
49         }
50 
51         void popFront() {
52             sql = sql[end .. $];
53             end = findEnd();
54         }
55 
56     private:
57         size_t findEnd() {
58             import std.algorithm : countUntil;
59             import std.string : toStringz;
60             import std.utf : byCodeUnit;
61 
62             size_t pos;
63             bool complete;
64             do {
65                 auto tail = sql[pos .. $];
66                 immutable offset = tail.byCodeUnit.countUntil(';') + 1;
67                 pos += offset;
68                 if (offset == 0)
69                     pos = sql.length;
70                 auto part = sql[0 .. pos];
71                 complete = cast(bool) sqlite3_complete(part.toStringz);
72             }
73             while (!complete && pos < sql.length);
74             return pos;
75         }
76     }
77 
78     return ByStatement(sql);
79 }
80 
81 unittest {
82     import std.algorithm : equal, map;
83     import std.string : strip;
84 
85     auto sql = "CREATE TABLE test (dummy);
86         CREATE TRIGGER trig INSERT ON test BEGIN SELECT 1; SELECT 'a;b'; END;
87         SELECT 'c;d';;
88         CREATE";
89     assert(equal(sql.byStatement.map!(s => s.strip), ["CREATE TABLE test (dummy);",
90             "CREATE TRIGGER trig INSERT ON test BEGIN SELECT 1; SELECT 'a;b'; END;",
91             "SELECT 'c;d';", ";", "CREATE"]));
92 }
93 
94 // getValue and setResult function templates
95 // used by createFunction and createAggregate
96 
97 auto getValue(T)(sqlite3_value* argv) if (isBoolean!T) {
98     return sqlite3_value_int64(argv) != 0;
99 }
100 
101 auto getValue(T)(sqlite3_value* argv) if (isIntegral!T) {
102     import std.conv : to;
103 
104     return sqlite3_value_int64(argv).to!T;
105 }
106 
107 auto getValue(T)(sqlite3_value* argv) if (isFloatingPoint!T) {
108     import std.conv : to;
109 
110     if (sqlite3_value_type(argv) == SQLITE_NULL)
111         return double.nan;
112     return sqlite3_value_double(argv).to!T;
113 }
114 
115 auto getValue(T)(sqlite3_value* argv) if (isSomeString!T) {
116     import std.conv : to;
117 
118     return (cast(const(char)*) sqlite3_value_text(argv)).to!T;
119 }
120 
121 auto getValue(T)(sqlite3_value* argv) if (isArray!T && !isSomeString!T) {
122     import std.conv : to;
123     import core.stdc.string : memcpy;
124 
125     auto n = sqlite3_value_bytes(argv);
126     ubyte[] blob;
127     blob.length = n;
128     memcpy(blob.ptr, sqlite3_value_blob(argv), n);
129     return cast(T) blob;
130 }
131 
132 auto getValue(T : Nullable!U, U...)(sqlite3_value* argv) {
133     if (sqlite3_value_type(argv) == SQLITE_NULL)
134         return T.init;
135     return T(getValue!(U[0])(argv));
136 }
137 
138 void setResult(T)(sqlite3_context* context, T value)
139         if (isIntegral!T || isBoolean!T) {
140     import std.conv : to;
141 
142     sqlite3_result_int64(context, value.to!long);
143 }
144 
145 void setResult(T)(sqlite3_context* context, T value) if (isFloatingPoint!T) {
146     import std.conv : to;
147 
148     sqlite3_result_double(context, value.to!double);
149 }
150 
151 void setResult(T)(sqlite3_context* context, T value) if (isSomeString!T) {
152     import std.conv : to;
153 
154     auto val = value.to!string;
155     sqlite3_result_text64(context, cast(const(char)*) anchorMem(cast(void*) val.ptr),
156             val.length, &releaseMem, SQLITE_UTF8);
157 }
158 
159 void setResult(T)(sqlite3_context* context, T value)
160         if (isDynamicArray!T && !isSomeString!T) {
161     auto val = cast(void[]) value;
162     sqlite3_result_blob64(context, anchorMem(val.ptr), val.length, &releaseMem);
163 }
164 
165 void setResult(T)(sqlite3_context* context, T value) if (isStaticArray!T) {
166     auto val = cast(void[]) value;
167     sqlite3_result_blob64(context, val.ptr, val.sizeof, SQLITE_TRANSIENT);
168 }
169 
170 void setResult(T : Nullable!U, U...)(sqlite3_context* context, T value) {
171     if (value.isNull)
172         sqlite3_result_null(context);
173     else
174         setResult(context, value.get);
175 }
176 
177 string nothrowFormat(Args...)(string fmt, Args args) nothrow {
178     import std.string : format;
179 
180     try
181         return fmt.format(args);
182     catch (Exception e)
183         throw new Error("Error: " ~ e.msg);
184 }