Skip to content
11 changes: 10 additions & 1 deletion source/mysql/impl/prepared.d
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import mysql.protocol.packets;
import mysql.types;
import mysql.impl.result;
import mysql.safe.commands : ColumnSpecialization, CSN;
import std.datetime : DateTime;

/++
A struct to represent specializations of prepared statement parameters.
Expand Down Expand Up @@ -153,11 +154,19 @@ public:

enforce!MYX(index < _numParams, "Parameter index out of range.");

_inParams[index] = val;
static if ((is (T == DateTime)) || (is (T == const(DateTime)))|| (is (T == immutable(DateTime))))
{
_inParams[index] = DateTimeExt(val,0);
}
else
{
_inParams[index] = val;
}
psn.pIndex = index;
_psa[index] = psn;
}


///ditto
void setArg(T)(size_t index, Nullable!T val, SafeParameterSpecialization psn = SPSN.init)
{
Expand Down
58 changes: 38 additions & 20 deletions source/mysql/protocol/packet_helpers.d
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ ubyte[] pack(in Date dt) pure nothrow
}

/++
Function to extract a DateTime from a binary encoded row.
Function to extract a DateTimeExt from a binary encoded row.

Time/date structures are packed by the server into a byte sub-packet
with a leading length byte, and a minimal number of bytes to embody the data.
Expand All @@ -227,28 +227,31 @@ Params: a = slice of a protocol packet beginning at the length byte for a
chunk of DateTime data
Returns: A populated or default initialized `std.datetime.DateTime` struct.
+/
DateTime toDateTime(in ubyte[] a) pure
DateTimeExt toDateTime(in ubyte[] a) pure
{
enforce!MYXProtocol(a.length, "Supplied byte array is zero length");
if (a[0] == 0)
return DateTime();
return DateTimeExt(DateTimeExt(),0);

enforce!MYXProtocol(a[0] >= 4, "Supplied ubyte[] is not long enough");
int year = (a[2] << 8) + a[1];
int month = a[3];
int day = a[4];
DateTime dt;
DateTimeExt dt;
if (a[0] == 4)
{
dt = DateTime(year, month, day);
dt = DateTimeExt(DateTime(year, month, day),0);
}
else
{
enforce!MYXProtocol(a[0] >= 7, "Supplied ubyte[] is not long enough");
int hour = a[5];
int minute = a[6];
int second = a[7];
dt = DateTime(year, month, day, hour, minute, second);
int millisecond = 0;
if (a[0] >= 11)
millisecond = (a[11] << 24) + (a[10] << 16) + (a[9] << 8) + a[8];
dt = DateTimeExt(DateTime(year, month, day, hour, minute, second),millisecond);
}
return dt;
}
Expand All @@ -261,7 +264,7 @@ Text representations of a DateTime are as in 2011-11-11 12:20:02
Params: s = A string representation of the time difference.
Returns: A populated or default initialized `std.datetime.DateTime` struct.
+/
DateTime toDateTime(const(char)[] s)
DateTimeExt toDateTime(const(char)[] s)
{
int year = parse!(ushort)(s);
enforce!MYXProtocol(s.skipOver("-"), `Expected: "-"`);
Expand All @@ -274,18 +277,18 @@ DateTime toDateTime(const(char)[] s)
int minute = parse!(ubyte)(s);
enforce!MYXProtocol(s.skipOver(":"), `Expected: ":"`);
int second = parse!(ubyte)(s);
return DateTime(year, month, day, hour, minute, second);
return DateTimeExt(DateTime(year, month, day, hour, minute, second),0);
}

/++
Function to extract a DateTime from a ulong.
Function to extract a DateTimeExt from a ulong.

This is used to support the TimeStamp struct.

Params: x = A ulong e.g. 20111111122002UL.
Returns: A populated `std.datetime.DateTime` struct.
+/
DateTime toDateTime(ulong x)
DateTimeExt toDateTime(ulong x)
{
int second = cast(int) (x%100);
x /= 100;
Expand All @@ -302,7 +305,7 @@ DateTime toDateTime(ulong x)
enforce!MYXProtocol(year >= 1970 && year < 2039, "Date/time out of range for 2 bit timestamp");
enforce!MYXProtocol(year != 2038 || (month < 1 && day < 19 && hour < 3 && minute < 14 && second < 7),
"Date/time out of range for 2 bit timestamp");
return DateTime(year, month, day, hour, minute, second);
return DateTimeExt(DateTime(year, month, day, hour, minute, second),0);
}

/++
Expand All @@ -314,11 +317,12 @@ and a minimal number of bytes to embody the data.
Params: dt = `std.datetime.DateTime` struct.
Returns: Packed ubyte[].
+/
ubyte[] pack(in DateTime dt) pure nothrow
ubyte[] pack(in DateTimeExt dt) pure nothrow
{
uint len = 1;
if (dt.year || dt.month || dt.day) len = 5;
if (dt.hour || dt.minute|| dt.second) len = 8;
if (dt.u_seconds) len = 12;
ubyte[] rv;
rv.length = len;
rv[0] = cast(ubyte)(rv.length - 1); // num bytes
Expand All @@ -329,12 +333,19 @@ ubyte[] pack(in DateTime dt) pure nothrow
rv[3] = cast(ubyte) dt.month;
rv[4] = cast(ubyte) dt.day;
}
if(len == 8)
if(len >= 8)
{
rv[5] = cast(ubyte) dt.hour;
rv[6] = cast(ubyte) dt.minute;
rv[7] = cast(ubyte) dt.second;
}
if (len >= 12)
{
rv[8] = cast(ubyte)(dt.u_seconds & 0x0ff);
rv[9] = cast(ubyte)((dt.u_seconds >> 8) & 0x0ff);
rv[10] = cast(ubyte)((dt.u_seconds >> 16) & 0x0ff);
rv[11] = cast(ubyte)((dt.u_seconds >> 24) & 0x0ff);
}
return rv;
}

Expand Down Expand Up @@ -435,7 +446,7 @@ do
return toDate(packet.consume(5));
}

DateTime consume(T:DateTime, ubyte N=T.sizeof)(ref ubyte[] packet) pure
DateTimeExt consume(T:DateTimeExt, ubyte N=T.sizeof)(ref ubyte[] packet) pure
in
{
assert(packet.length);
Expand All @@ -445,7 +456,7 @@ do
{
auto numBytes = packet.consume!ubyte();
if(numBytes == 0)
return DateTime();
return DateTimeExt(DateTime(),0);

enforce!MYXProtocol(numBytes >= 4, "Supplied packet is not large enough to store DateTime");

Expand All @@ -455,14 +466,19 @@ do
int hour = 0;
int minute = 0;
int second = 0;
int u_seconds = 0;
if(numBytes > 4)
{
enforce!MYXProtocol(numBytes >= 7, "Supplied packet is not large enough to store a DateTime with TimeOfDay");
hour = packet.consume!ubyte();
minute = packet.consume!ubyte();
second = packet.consume!ubyte();
}
return DateTime(year, month, day, hour, minute, second);
if (numBytes >= 11)
{
u_seconds = packet.consume!uint;
}
return DateTimeExt(DateTime(year, month, day, hour, minute, second),u_seconds);
}


Expand Down Expand Up @@ -535,6 +551,8 @@ do
T myto(T)(const(char)[] value)
{
static if(is(T == DateTime))
return toDateTime(value).dt;
else static if(is(T == DateTimeExt))
return toDateTime(value);
else static if(is(T == Date))
return toDate(value);
Expand Down Expand Up @@ -575,8 +593,8 @@ SQLValue consumeBinaryValueIfComplete(T, int N=T.sizeof)(ref ubyte[] packet, boo
{
SQLValue result;

// Length of DateTime packet is NOT DateTime.sizeof, it can be 1, 5 or 8 bytes
static if(is(T==DateTime))
// Length of DateTime packet is NOT DateTime.sizeof, it can be 1, 5 or 8 or 12 bytes
static if(is(T==DateTimeExt))
result.isIncomplete = packet.length < 1;
else
result.isIncomplete = packet.length < N;
Expand Down Expand Up @@ -669,15 +687,15 @@ SQLValue consumeIfComplete()(ref ubyte[] packet, SQLType sqlType, bool binary, b
case SQLType.DOUBLE:
return packet.consumeIfComplete!double(binary, unsigned);
case SQLType.TIMESTAMP:
return packet.consumeIfComplete!DateTime(binary, unsigned);
return packet.consumeIfComplete!DateTimeExt(binary, unsigned);
case SQLType.TIME:
return packet.consumeIfComplete!TimeOfDay(binary, unsigned);
case SQLType.YEAR:
return packet.consumeIfComplete!ushort(binary, unsigned);
case SQLType.DATE:
return packet.consumeIfComplete!Date(binary, unsigned);
case SQLType.DATETIME:
return packet.consumeIfComplete!DateTime(binary, unsigned);
return packet.consumeIfComplete!DateTimeExt(binary, unsigned);
case SQLType.VARCHAR:
case SQLType.ENUM:
case SQLType.SET:
Expand Down
43 changes: 39 additions & 4 deletions source/mysql/types.d
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/// Structures for MySQL types not built-in to D/Phobos.
module mysql.types;
import taggedalgebraic.taggedalgebraic;
import std.datetime : DateTime, TimeOfDay, Date;
import std.datetime : DateTime, TimeOfDay, Date, SysTime,TimeZone,usecs;
import std.typecons : Nullable;
import std.format;

/++
A simple struct to represent time difference.
Expand Down Expand Up @@ -33,6 +34,40 @@ struct Timestamp
ulong rep;
}

/++
A D struct to stand for a DateTime with sub second resolution

+/
struct DateTimeExt
{
DateTime dt;
alias dt this;
uint u_seconds;
string toString() pure nothrow @safe const
{
try
return dt.date.toISOExtString ~ " " ~ dt.timeOfDay.toISOExtString ~ format(".%06d",this.u_seconds);
catch(Exception e)
assert(0,"DateTimeExt.toString threw");
}
SysTime makeSysTime(return scope immutable TimeZone tz = null) @safe const scope
{
return SysTime(dt,usecs(this.u_seconds),tz);
}
DateTimeExt opAssign(const DateTime x) @safe
{
dt = x;
u_seconds = 0;
return this;
}
DateTime opCast(T)()
if (is(T == DateTime))
{
return dt;
}
}


private union _MYTYPE
{
@safeOnly:
Expand All @@ -54,7 +89,7 @@ private union _MYTYPE
long Long;
float Float;
double Double;
.DateTime DateTime;
DateTimeExt DateTime;
TimeOfDay Time;
.Timestamp Timestamp;
.Date Date;
Expand All @@ -74,7 +109,7 @@ private union _MYTYPE
const(long)* LongRef;
const(float)* FloatRef;
const(double)* DoubleRef;
const(.DateTime)* DateTimeRef;
const(DateTimeExt)* DateTimeRef;
const(TimeOfDay)* TimeRef;
const(.Date)* DateRef;
const(string)* TextRef;
Expand Down Expand Up @@ -209,7 +244,7 @@ package MySQLVal _toVal(Variant v)
enum FQN = T.stringof;
}

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

Expand Down