Skip to content

Commit a6bed51

Browse files
committed
Fix #272. Blob types should be accessible via byte[] for unsafe
interface.
1 parent 0ea6cb3 commit a6bed51

File tree

4 files changed

+69
-16
lines changed

4 files changed

+69
-16
lines changed

integration-tests/source/mysql/test/common.d

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,16 @@ version(DoCoreTests)
122122

123123
// Timestamp is a bit special as it's converted to a DateTime when
124124
// returning from MySQL to avoid having to use a mysql specific type.
125+
//
126+
// byte[] is also special (for now) because it's supported with the
127+
// unsafe portion of prepared statements. However, it's always ubyte[]
128+
// underneath.
129+
//
130+
// TODO: remove this hack for byte[] when unsafe mysql-native is removed.
125131
static if(is(T == DateTime) && is(U == Timestamp))
126132
assert(result.get.get!DateTime == expected.toDateTime());
133+
else static if(is(T == byte[]))
134+
assert(cast(byte[])result.get.get!(ubyte[]) == expected);
127135
else
128136
assert(result.get.get!T == expected);
129137
}

integration-tests/source/mysql/test/integration.d

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -982,13 +982,20 @@ debug(MYSQLN_TESTS)
982982
assertBasicTests!string("TEXT", "", "aoeu");
983983
assertBasicTests!string("LONGTEXT", "", "aoeu");
984984

985-
assertBasicTests!(ubyte[])("TINYBLOB", "", "aoeu");
986-
assertBasicTests!(ubyte[])("MEDIUMBLOB", "", "aoeu");
987-
assertBasicTests!(ubyte[])("BLOB", "", "aoeu");
988-
assertBasicTests!(ubyte[])("LONGBLOB", "", "aoeu");
985+
import std.meta : AliasSeq;
986+
static if(doSafe) alias blobTypes = AliasSeq!(ubyte[]);
987+
else alias blobTypes = AliasSeq!(ubyte[], byte[]);
989988

990-
assertBasicTests!(ubyte[])("TINYBLOB", cast(ubyte[])"".dup, cast(ubyte[])"aoeu".dup);
991-
assertBasicTests!(ubyte[])("TINYBLOB", "".dup, "aoeu".dup);
989+
static foreach(BT; blobTypes)
990+
{
991+
assertBasicTests!(BT)("TINYBLOB", "", "aoeu");
992+
assertBasicTests!(BT)("MEDIUMBLOB", "", "aoeu");
993+
assertBasicTests!(BT)("BLOB", "", "aoeu");
994+
assertBasicTests!(BT)("LONGBLOB", "", "aoeu");
995+
996+
assertBasicTests!(BT)("TINYBLOB", cast(BT)"".dup, cast(BT)"aoeu".dup);
997+
assertBasicTests!(BT)("TINYBLOB", "".dup, "aoeu".dup);
998+
}
992999

9931000
assertBasicTests!Date("DATE", Date(2013, 10, 03));
9941001
assertBasicTests!DateTime("DATETIME", DateTime(2013, 10, 03, 12, 55, 35));

source/mysql/impl/prepared.d

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,13 @@ struct UnsafePrepared
324324
void setArg(T)(size_t index, T val, UnsafeParameterSpecialization psn = UPSN.init) @system
325325
if(!is(T == Variant))
326326
{
327-
_safe.setArg(index, val, cast(SPSN)psn);
327+
// forward to the safe API, but if not, fall back on what the unsafe
328+
// version did.
329+
static if(__traits(compiles, _safe.setArg(index, val, cast(SPSN)psn)))
330+
_safe.setArg(index, val, cast(SPSN)psn);
331+
else
332+
// convert to variant first, then rely on the runtime to catch it.
333+
setArg(index, Variant(val), psn);
328334
}
329335

330336
/// ditto
@@ -342,8 +348,13 @@ struct UnsafePrepared
342348
auto translateArg(alias arg)() {
343349
static if(is(typeof(arg) == Variant))
344350
return _toVal(arg);
345-
else
351+
else static if(__traits(compiles, setArg(0, arg)))
346352
return arg;
353+
else
354+
// not settable using the safe API, convert to variant first,
355+
// and then use the variant conversion routine to flesh out any
356+
// cases.
357+
return _toVal(Variant(arg));
347358
}
348359
_safe.setArgs(staticMap!(translateArg, args));
349360
}
@@ -581,4 +592,3 @@ package(mysql) struct PreparedRegistrations(Payload)
581592
return info;
582593
}
583594
}
584-

source/mysql/types.d

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ $(SAFE_MIGRATION)
183183
alias MySQLVal = TaggedAlgebraic!_MYTYPE;
184184

185185
// helper to convert variants to MySQLVal. Used wherever variant is still used.
186-
import std.variant : Variant;
186+
private import std.variant : Variant;
187187
package MySQLVal _toVal(Variant v)
188188
{
189189
int x;
@@ -197,18 +197,36 @@ package MySQLVal _toVal(Variant v)
197197
}
198198

199199
import std.meta;
200-
import std.traits;
201200
import mysql.exceptions;
201+
import std.traits : Unqual;
202+
// much simpler/focused fullyqualifiedname template
203+
template FQN(T) {
204+
static if(is(T == DateTime) || is(T == Date) || is(T == TimeOfDay))
205+
enum FQN = "std.datetime.date." ~ T.stringof;
206+
else static if(is(T == Timestamp))
207+
enum FQN = "mysql.types.Timestamp";
208+
else
209+
enum FQN = T.stringof;
210+
}
211+
202212
alias BasicTypes = AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, DateTime, TimeOfDay, Date, Timestamp);
203-
alias ArrayTypes = AliasSeq!(char[], const(char)[], ubyte[], const(ubyte)[], immutable(ubyte)[]);
213+
alias ArrayTypes = AliasSeq!(char[], const(char)[],
214+
ubyte[], const(ubyte)[], immutable(ubyte)[]);
215+
216+
// types that worked with the old system via Variant, but have to be
217+
// converted to work with MySQLVal
218+
alias ConvertibleTypes = AliasSeq!(byte[], const(byte)[], immutable(byte)[]);
219+
alias ConvertedTypes = AliasSeq!(const(ubyte[]), const(ubyte[]), const(ubyte[]) );
220+
static assert(ConvertibleTypes.length == ConvertedTypes.length);
221+
204222
switch (ts)
205223
{
206224
static foreach(Type; BasicTypes)
207225
{
208-
case fullyQualifiedName!Type:
209-
case "const(" ~ fullyQualifiedName!Type ~ ")":
210-
case "immutable(" ~ fullyQualifiedName!Type ~ ")":
211-
case "shared(immutable(" ~ fullyQualifiedName!Type ~ "))":
226+
case FQN!Type:
227+
case "const(" ~ FQN!Type ~ ")":
228+
case "immutable(" ~ FQN!Type ~ ")":
229+
case "shared(immutable(" ~ FQN!Type ~ "))":
212230
if(isRef)
213231
return MySQLVal(v.get!(const(Type*)));
214232
else
@@ -225,6 +243,16 @@ package MySQLVal _toVal(Variant v)
225243
return MySQLVal(v.get!(Type));
226244
}
227245
}
246+
static foreach(i; 0 .. ConvertibleTypes.length)
247+
{
248+
case ConvertibleTypes[i].stringof:
249+
{
250+
if(isRef)
251+
return MySQLVal(cast(ConvertedTypes[i]*)v.get!(ConvertibleTypes[i]*));
252+
else
253+
return MySQLVal(cast(ConvertedTypes[i])v.get!(ConvertibleTypes[i]));
254+
}
255+
}
228256
case "immutable(char)[]":
229257
// have to do this separately, because everything says "string" but
230258
// Variant says "immutable(char)[]"

0 commit comments

Comments
 (0)