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     import std.conv : to;
25 
26     return sqlite3_errmsg(db).to!string;
27 }
28 
29 string errmsg(sqlite3_stmt* stmt) nothrow {
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() const @safe pure nothrow @nogc {
44             return !sql.length;
45         }
46 
47         string front() const @safe pure nothrow @nogc {
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), [
90                 "CREATE TABLE test (dummy);",
91                 "CREATE TRIGGER trig INSERT ON test BEGIN SELECT 1; SELECT 'a;b'; END;",
92                 "SELECT 'c;d';", ";", "CREATE"
93             ]));
94 }
95 
96 // getValue and setResult function templates
97 // used by createFunction and createAggregate
98 
99 auto getValue(T)(sqlite3_value* argv) if (isBoolean!T) {
100     return sqlite3_value_int64(argv) != 0;
101 }
102 
103 auto getValue(T)(sqlite3_value* argv) if (isIntegral!T) {
104     import std.conv : to;
105 
106     return sqlite3_value_int64(argv).to!T;
107 }
108 
109 auto getValue(T)(sqlite3_value* argv) if (isFloatingPoint!T) {
110     import std.conv : to;
111 
112     if (sqlite3_value_type(argv) == SQLITE_NULL)
113         return double.nan;
114     return sqlite3_value_double(argv).to!T;
115 }
116 
117 auto getValue(T)(sqlite3_value* argv) if (isSomeString!T) {
118     import std.conv : to;
119 
120     return (cast(const(char)*) sqlite3_value_text(argv)).to!T;
121 }
122 
123 auto getValue(T)(sqlite3_value* argv) if (isArray!T && !isSomeString!T) {
124     import std.conv : to;
125     import core.stdc.string : memcpy;
126 
127     auto n = sqlite3_value_bytes(argv);
128     ubyte[] blob;
129     blob.length = n;
130     memcpy(blob.ptr, sqlite3_value_blob(argv), n);
131     return cast(T) blob;
132 }
133 
134 auto getValue(T : Nullable!U, U...)(sqlite3_value* argv) {
135     if (sqlite3_value_type(argv) == SQLITE_NULL)
136         return T.init;
137     return T(getValue!(U[0])(argv));
138 }
139 
140 void setResult(T)(sqlite3_context* context, T value)
141         if (isIntegral!T || isBoolean!T) {
142     import std.conv : to;
143 
144     sqlite3_result_int64(context, value.to!long);
145 }
146 
147 void setResult(T)(sqlite3_context* context, T value) if (isFloatingPoint!T) {
148     import std.conv : to;
149 
150     sqlite3_result_double(context, value.to!double);
151 }
152 
153 void setResult(T)(sqlite3_context* context, T value) if (isSomeString!T) {
154     import std.conv : to;
155 
156     auto val = value.to!string;
157     sqlite3_result_text64(context, cast(const(char)*) anchorMem(cast(void*) val.ptr),
158             val.length, &releaseMem, SQLITE_UTF8);
159 }
160 
161 void setResult(T)(sqlite3_context* context, T value)
162         if (isDynamicArray!T && !isSomeString!T) {
163     auto val = cast(void[]) value;
164     sqlite3_result_blob64(context, anchorMem(val.ptr), val.length, &releaseMem);
165 }
166 
167 void setResult(T)(sqlite3_context* context, T value) if (isStaticArray!T) {
168     auto val = cast(void[]) value;
169     sqlite3_result_blob64(context, val.ptr, val.sizeof, SQLITE_TRANSIENT);
170 }
171 
172 void setResult(T : Nullable!U, U...)(sqlite3_context* context, T value) {
173     if (value.isNull)
174         sqlite3_result_null(context);
175     else
176         setResult(context, value.get);
177 }
178 
179 string nothrowFormat(Args...)(string fmt, Args args) nothrow {
180     import std.string : format;
181 
182     try
183         return fmt.format(args);
184     catch (Exception e)
185         throw new Error("Error: " ~ e.msg);
186 }