Skip to content

Fix #272. Blob types should be accessible via byte[] #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions integration-tests/source/mysql/test/common.d
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,16 @@ version(DoCoreTests)

// Timestamp is a bit special as it's converted to a DateTime when
// returning from MySQL to avoid having to use a mysql specific type.
//
// byte[] is also special (for now) because it's supported with the
// unsafe portion of prepared statements. However, it's always ubyte[]
// underneath.
//
// TODO: remove this hack for byte[] when unsafe mysql-native is removed.
static if(is(T == DateTime) && is(U == Timestamp))
assert(result.get.get!DateTime == expected.toDateTime());
else static if(is(T == byte[]))
assert(cast(byte[])result.get.get!(ubyte[]) == expected);
else
assert(result.get.get!T == expected);
}
Expand Down
19 changes: 13 additions & 6 deletions integration-tests/source/mysql/test/integration.d
Original file line number Diff line number Diff line change
Expand Up @@ -982,13 +982,20 @@ debug(MYSQLN_TESTS)
assertBasicTests!string("TEXT", "", "aoeu");
assertBasicTests!string("LONGTEXT", "", "aoeu");

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

assertBasicTests!(ubyte[])("TINYBLOB", cast(ubyte[])"".dup, cast(ubyte[])"aoeu".dup);
assertBasicTests!(ubyte[])("TINYBLOB", "".dup, "aoeu".dup);
static foreach(BT; blobTypes)
{
assertBasicTests!(BT)("TINYBLOB", "", "aoeu");
assertBasicTests!(BT)("MEDIUMBLOB", "", "aoeu");
assertBasicTests!(BT)("BLOB", "", "aoeu");
assertBasicTests!(BT)("LONGBLOB", "", "aoeu");

assertBasicTests!(BT)("TINYBLOB", cast(BT)"".dup, cast(BT)"aoeu".dup);
assertBasicTests!(BT)("TINYBLOB", "".dup, "aoeu".dup);
}

assertBasicTests!Date("DATE", Date(2013, 10, 03));
assertBasicTests!DateTime("DATETIME", DateTime(2013, 10, 03, 12, 55, 35));
Expand Down
16 changes: 13 additions & 3 deletions source/mysql/impl/prepared.d
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,13 @@ struct UnsafePrepared
void setArg(T)(size_t index, T val, UnsafeParameterSpecialization psn = UPSN.init) @system
if(!is(T == Variant))
{
_safe.setArg(index, val, cast(SPSN)psn);
// forward to the safe API, but if not, fall back on what the unsafe
// version did.
static if(__traits(compiles, _safe.setArg(index, val, cast(SPSN)psn)))
_safe.setArg(index, val, cast(SPSN)psn);
else
// convert to variant first, then rely on the runtime to catch it.
setArg(index, Variant(val), psn);
}

/// ditto
Expand All @@ -342,8 +348,13 @@ struct UnsafePrepared
auto translateArg(alias arg)() {
static if(is(typeof(arg) == Variant))
return _toVal(arg);
else
else static if(__traits(compiles, setArg(0, arg)))
return arg;
else
// not settable using the safe API, convert to variant first,
// and then use the variant conversion routine to flesh out any
// cases.
return _toVal(Variant(arg));
}
_safe.setArgs(staticMap!(translateArg, args));
}
Expand Down Expand Up @@ -581,4 +592,3 @@ package(mysql) struct PreparedRegistrations(Payload)
return info;
}
}

42 changes: 35 additions & 7 deletions source/mysql/types.d
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ $(SAFE_MIGRATION)
alias MySQLVal = TaggedAlgebraic!_MYTYPE;

// helper to convert variants to MySQLVal. Used wherever variant is still used.
import std.variant : Variant;
private import std.variant : Variant;
package MySQLVal _toVal(Variant v)
{
int x;
Expand All @@ -197,18 +197,36 @@ package MySQLVal _toVal(Variant v)
}

import std.meta;
import std.traits;
import mysql.exceptions;
import std.traits : Unqual;
// much simpler/focused fullyqualifiedname template
template FQN(T) {
static if(is(T == DateTime) || is(T == Date) || is(T == TimeOfDay))
enum FQN = "std.datetime.date." ~ T.stringof;
else static if(is(T == Timestamp))
enum FQN = "mysql.types.Timestamp";
else
enum FQN = T.stringof;
}

alias BasicTypes = AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, DateTime, TimeOfDay, Date, Timestamp);
alias ArrayTypes = AliasSeq!(char[], const(char)[], ubyte[], const(ubyte)[], immutable(ubyte)[]);
alias ArrayTypes = AliasSeq!(char[], const(char)[],
ubyte[], const(ubyte)[], immutable(ubyte)[]);

// types that worked with the old system via Variant, but have to be
// converted to work with MySQLVal
alias ConvertibleTypes = AliasSeq!(byte[], const(byte)[], immutable(byte)[]);
alias ConvertedTypes = AliasSeq!(const(ubyte[]), const(ubyte[]), const(ubyte[]) );
static assert(ConvertibleTypes.length == ConvertedTypes.length);

switch (ts)
{
static foreach(Type; BasicTypes)
{
case fullyQualifiedName!Type:
case "const(" ~ fullyQualifiedName!Type ~ ")":
case "immutable(" ~ fullyQualifiedName!Type ~ ")":
case "shared(immutable(" ~ fullyQualifiedName!Type ~ "))":
case FQN!Type:
case "const(" ~ FQN!Type ~ ")":
case "immutable(" ~ FQN!Type ~ ")":
case "shared(immutable(" ~ FQN!Type ~ "))":
if(isRef)
return MySQLVal(v.get!(const(Type*)));
else
Expand All @@ -225,6 +243,16 @@ package MySQLVal _toVal(Variant v)
return MySQLVal(v.get!(Type));
}
}
static foreach(i; 0 .. ConvertibleTypes.length)
{
case ConvertibleTypes[i].stringof:
{
if(isRef)
return MySQLVal(cast(ConvertedTypes[i]*)v.get!(ConvertibleTypes[i]*));
else
return MySQLVal(cast(ConvertedTypes[i])v.get!(ConvertibleTypes[i]));
}
}
case "immutable(char)[]":
// have to do this separately, because everything says "string" but
// Variant says "immutable(char)[]"
Expand Down