Skip to content

Commit a569830

Browse files
committed
Moving files, add the new type.
1 parent 5ceed3c commit a569830

File tree

7 files changed

+305
-1
lines changed

7 files changed

+305
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dub.selections.json
77
*.lib
88
*.dll
99
*.exe
10+
.*.swp
1011
/bin
1112
/testConnectionStr.txt
1213

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

source/mysql/types.d

Lines changed: 304 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
/// Structures for MySQL types not built-in to D/Phobos.
1+
/// Structures for MySQL types not built-in to D/Phobos.
22
module mysql.types;
3+
import taggedalgebraic.taggedalgebraic;
4+
import std.datetime : DateTime, TimeOfDay, Date;
5+
import std.typecons : Nullable;
36

47
/++
58
A simple struct to represent time difference.
@@ -29,3 +32,303 @@ struct Timestamp
2932
{
3033
ulong rep;
3134
}
35+
36+
private union _MYTYPE
37+
{
38+
@safeOnly:
39+
// blobs are const because of the indirection. In this case, it's not
40+
// important because nobody is going to use MySQLVal to maintain their
41+
// ubyte array.
42+
ubyte[] Blob;
43+
const(ubyte)[] CBlob;
44+
45+
typeof(null) Null;
46+
bool Bit;
47+
ubyte UByte;
48+
byte Byte;
49+
ushort UShort;
50+
short Short;
51+
uint UInt;
52+
int Int;
53+
ulong ULong;
54+
long Long;
55+
float Float;
56+
double Double;
57+
.DateTime DateTime;
58+
TimeOfDay Time;
59+
.Timestamp Timestamp;
60+
.Date Date;
61+
62+
@disableIndex string Text;
63+
@disableIndex const(char)[] CText;
64+
65+
// pointers
66+
const(bool)* BitRef;
67+
const(ubyte)* UByteRef;
68+
const(byte)* ByteRef;
69+
const(ushort)* UShortRef;
70+
const(short)* ShortRef;
71+
const(uint)* UIntRef;
72+
const(int)* IntRef;
73+
const(ulong)* ULongRef;
74+
const(long)* LongRef;
75+
const(float)* FloatRef;
76+
const(double)* DoubleRef;
77+
const(.DateTime)* DateTimeRef;
78+
const(TimeOfDay)* TimeRef;
79+
const(.Date)* DateRef;
80+
const(string)* TextRef;
81+
const(char[])* CTextRef;
82+
const(ubyte[])* BlobRef;
83+
const(.Timestamp)* TimestampRef;
84+
}
85+
86+
/++
87+
MySQLVal is MySQL's tagged algebraic type that supports only @safe usage
88+
(see $(LINK2, http://code.dlang.org/packages/taggedalgebraic, TaggedAlgebraic)
89+
for more information on the features of this type). Note that TaggedAlgebraic
90+
has UFCS methods that are not available without importing that module in your
91+
code.
92+
93+
The type can hold any possible type that MySQL can use or return. The _MYTYPE
94+
union, which is a private union for the project, defines the names of the types
95+
that can be stored. These names double as the names for the MySQLVal.Kind
96+
enumeration. To that end, this is the entire union definition:
97+
98+
------
99+
private union _MYTYPE
100+
{
101+
ubyte[] Blob;
102+
const(ubyte)[] CBlob;
103+
104+
typeof(null) Null;
105+
bool Bit;
106+
ubyte UByte;
107+
byte Byte;
108+
ushort UShort;
109+
short Short;
110+
uint UInt;
111+
int Int;
112+
ulong ULong;
113+
long Long;
114+
float Float;
115+
double Double;
116+
std.datetime.DateTime DateTime;
117+
std.datetime.TimeOfDay Time;
118+
mysql.types.Timestamp Timestamp;
119+
std.datetime.Date Date;
120+
121+
string Text;
122+
const(char)[] CText;
123+
124+
// pointers
125+
const(bool)* BitRef;
126+
const(ubyte)* UByteRef;
127+
const(byte)* ByteRef;
128+
const(ushort)* UShortRef;
129+
const(short)* ShortRef;
130+
const(uint)* UIntRef;
131+
const(int)* IntRef;
132+
const(ulong)* ULongRef;
133+
const(long)* LongRef;
134+
const(float)* FloatRef;
135+
const(double)* DoubleRef;
136+
const(DateTime)* DateTimeRef;
137+
const(TimeOfDay)* TimeRef;
138+
const(Date)* DateRef;
139+
const(string)* TextRef;
140+
const(char[])* CTextRef;
141+
const(ubyte[])* BlobRef;
142+
const(Timestamp)* TimestampRef;
143+
}
144+
------
145+
146+
Note that the pointers are all const, as the only use case in mysql-native for them is as rebindable parameters to a Prepared struct.
147+
148+
MySQLVal allows operations, field, and member function access for each of the supported types without unwrapping the MySQLVal value. For example:
149+
150+
------
151+
import mysql.safe;
152+
153+
// support for comparison is valid for any type that supports it
154+
assert(conn.queryValue("SELECT COUNT(*) FROM sometable") > 20);
155+
156+
// access members of supporting types without unwrapping or verifying type first
157+
assert(conn.queryValue("SELECT updated_date FROM someTable WHERE id=5").year == 2020);
158+
159+
// arithmetic is supported, return type may vary
160+
auto val = conn.queryValue("SELECT some_integer FROM sometable WHERE id=5") + 100;
161+
static assert(is(typeof(val) == MySQLVal));
162+
assert(val.kind == MySQLVal.Kind.Int);
163+
164+
// this will be a double and not a MySQLVal, because all types that support
165+
// addition with a double result in a double.
166+
auto val2 = conn.queryValue("SELECT some_float FROM sometable WHERE id=5") + 100.0;
167+
static assert(is(typeof(val2) == double));
168+
------
169+
170+
MySQLVal is used in all operations interally for mysql-native, and explicitly
171+
for all safe API calls. Version 3.0.x and earlier of the mysql-native library
172+
used Variant, so this module provides multiple shims to allow code to "just
173+
work", and also provides conversion back to Variant.
174+
175+
$(SAFE_MIGRATION)
176+
+/
177+
alias MySQLVal = TaggedAlgebraic!_MYTYPE;
178+
179+
// helper to convert variants to MySQLVal. Used wherever variant is still used.
180+
import std.variant : Variant;
181+
package MySQLVal _toVal(Variant v)
182+
{
183+
int x;
184+
// unfortunately, we need to use a giant switch. But hopefully people will stop using Variant, and this will go away.
185+
string ts = v.type.toString();
186+
bool isRef;
187+
if (ts[$-1] == '*')
188+
{
189+
ts.length = ts.length-1;
190+
isRef= true;
191+
}
192+
193+
import std.meta;
194+
import std.traits;
195+
import mysql.exceptions;
196+
alias BasicTypes = AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, DateTime, TimeOfDay, Date, Timestamp);
197+
alias ArrayTypes = AliasSeq!(char[], const(char)[], ubyte[], const(ubyte)[], immutable(ubyte)[]);
198+
switch (ts)
199+
{
200+
static foreach(Type; BasicTypes)
201+
{
202+
case fullyQualifiedName!Type:
203+
case "const(" ~ fullyQualifiedName!Type ~ ")":
204+
case "immutable(" ~ fullyQualifiedName!Type ~ ")":
205+
case "shared(immutable(" ~ fullyQualifiedName!Type ~ "))":
206+
if(isRef)
207+
return MySQLVal(v.get!(const(Type*)));
208+
else
209+
return MySQLVal(v.get!(const(Type)));
210+
}
211+
static foreach(Type; ArrayTypes)
212+
{
213+
case Type.stringof:
214+
{
215+
alias ET = Unqual!(typeof(Type.init[0]));
216+
if(isRef)
217+
return MySQLVal(v.get!(const(ET[]*)));
218+
else
219+
return MySQLVal(v.get!(Type));
220+
}
221+
}
222+
case "immutable(char)[]":
223+
// have to do this separately, because everything says "string" but
224+
// Variant says "immutable(char)[]"
225+
if(isRef)
226+
return MySQLVal(v.get!(const(char[]*)));
227+
else
228+
return MySQLVal(v.get!(string));
229+
case "typeof(null)":
230+
return MySQLVal(null);
231+
default:
232+
throw new MYX("Unsupported Database Variant Type: " ~ ts);
233+
}
234+
}
235+
236+
/++
237+
Convert a MySQLVal into a Variant. This provides a backwards-compatible shim to use if necessary when transitioning to the safe API.
238+
239+
$(SAFE_MIGRATION)
240+
+/
241+
Variant asVariant(MySQLVal v)
242+
{
243+
return v.apply!((a) => Variant(a));
244+
}
245+
246+
/// ditto
247+
Nullable!Variant asVariant(Nullable!MySQLVal v)
248+
{
249+
if(v.isNull)
250+
return Nullable!Variant();
251+
return Nullable!Variant(v.get.asVariant);
252+
}
253+
254+
/++
255+
Compatibility layer for MySQLVal. These functions provide methods that
256+
$(LINK2, http://code.dlang.org/packages/taggedalgebraic, TaggedAlgebraic)
257+
does not provide in order to keep functionality that was available with Variant.
258+
259+
Notes:
260+
261+
The `type` shim should be avoided in favor of using the `kind` property of
262+
TaggedAlgebraic.
263+
264+
The `get` shim works differently than the TaggedAlgebraic version, as the
265+
Variant get function would provide implicit type conversions, but the
266+
TaggedAlgebraic version did not.
267+
268+
All shims other than `type` will likely remain as convenience features.
269+
270+
Note that `peek` is inferred @system because it returns a pointer to the
271+
provided value.
272+
273+
$(SAFE_MIGRATION)
274+
+/
275+
bool convertsTo(T)(ref MySQLVal val)
276+
{
277+
return val.apply!((a) => is(typeof(a) : T));
278+
}
279+
280+
/// ditto
281+
T get(T)(auto ref MySQLVal val)
282+
{
283+
static T convert(V)(ref V v)
284+
{
285+
static if(is(V : T))
286+
return v;
287+
else
288+
{
289+
import mysql.exceptions;
290+
throw new MYX("Cannot get type " ~ T.stringof ~ " with MySQLVal storing type " ~ V.stringof);
291+
}
292+
}
293+
return val.apply!convert();
294+
}
295+
296+
/// ditto
297+
T coerce(T)(auto ref MySQLVal val)
298+
{
299+
import std.conv : to;
300+
static T convert(V)(ref V v)
301+
{
302+
static if(is(V : T))
303+
{
304+
return v;
305+
}
306+
else static if(is(typeof(v.to!T())))
307+
{
308+
return v.to!T;
309+
}
310+
else
311+
{
312+
import mysql.exceptions;
313+
throw new MYX("Cannot coerce type " ~ V.stringof ~ " into type " ~ T.stringof);
314+
}
315+
}
316+
return val.apply!convert();
317+
}
318+
319+
/// ditto
320+
TypeInfo type(MySQLVal val) @safe pure nothrow
321+
{
322+
return val.apply!((ref v) => typeid(v));
323+
}
324+
325+
/// ditto
326+
T *peek(T)(ref MySQLVal val)
327+
{
328+
// use exact type.
329+
import taggedalgebraic.taggedalgebraic : get;
330+
if(val.hasType!T)
331+
return &val.get!T;
332+
return null;
333+
}
334+
File renamed without changes.

0 commit comments

Comments
 (0)