Skip to content

Commit

Permalink
duration data type
Browse files Browse the repository at this point in the history
  • Loading branch information
speed2exe committed Dec 25, 2023
1 parent a5914e6 commit d4bedd7
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 15 deletions.
36 changes: 27 additions & 9 deletions integration_tests/client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const ErrorPacket = @import("../src/protocol.zig").generic_response.ErrorPacket;
const minInt = std.math.minInt;
const maxInt = std.math.maxInt;
const DateTime = @import("../src/temporal.zig").DateTime;
const Duration = @import("../src/temporal.zig").Duration;

// convenient function for testing
fn queryExpectOk(c: *Client, query: []const u8) !void {
Expand Down Expand Up @@ -444,12 +445,15 @@ test "binary data types - temporal" {
\\CREATE TABLE test.temporal_types_example (
\\ event_time DATETIME(6) NOT NULL,
\\ event_time2 DATETIME(2) NOT NULL,
\\ event_time3 DATETIME NOT NULL
\\ event_time3 DATETIME NOT NULL,
\\ duration TIME(6) NOT NULL,
\\ duration2 TIME(4) NOT NULL,
\\ duration3 TIME NOT NULL
\\)
);
defer queryExpectOk(&c, "DROP TABLE test.temporal_types_example") catch {};

const prep_res = try c.prepare(allocator, "INSERT INTO test.temporal_types_example VALUES (?, ?, ?)");
const prep_res = try c.prepare(allocator, "INSERT INTO test.temporal_types_example VALUES (?, ?, ?, ?, ?, ?)");
defer prep_res.deinit(allocator);
const prep_stmt = try prep_res.expect(.ok);

Expand All @@ -462,7 +466,7 @@ test "binary data types - temporal" {
.second = 58,
.microsecond = 123456,
};
const without_ms: DateTime = .{
const datetime_no_ms: DateTime = .{
.year = 2023,
.month = 11,
.day = 30,
Expand All @@ -475,11 +479,25 @@ test "binary data types - temporal" {
.month = 11,
.day = 30,
};
const my_duration: Duration = .{
.days = 1,
.hours = 23,
.minutes = 59,
.seconds = 59,
.microseconds = 123456,
};
const duration_no_ms: Duration = .{
.days = 1,
.hours = 23,
.minutes = 59,
.seconds = 59,
};
const duration_zero: Duration = .{};

const params = .{
.{ my_time, my_time, my_time },
.{ without_ms, without_ms, without_ms },
.{ only_day, only_day, only_day },
.{ my_time, my_time, my_time, my_duration, my_duration, my_duration },
.{ datetime_no_ms, datetime_no_ms, datetime_no_ms, duration_no_ms, duration_no_ms, duration_no_ms },
.{ only_day, only_day, only_day, duration_zero, duration_zero, duration_zero },
};

inline for (params) |param| {
Expand All @@ -497,9 +515,9 @@ test "binary data types - temporal" {
defer table.deinit(allocator);

const expected: []const []const ?[]const u8 = &.{
&.{ "2023-11-30 06:50:58.123456", "2023-11-30 06:50:58.12", "2023-11-30 06:50:58" },
&.{ "2023-11-30 06:50:58.000000", "2023-11-30 06:50:58.00", "2023-11-30 06:50:58" },
&.{ "2023-11-30 00:00:00.000000", "2023-11-30 00:00:00.00", "2023-11-30 00:00:00" },
&.{ "2023-11-30 06:50:58.123456", "2023-11-30 06:50:58.12", "2023-11-30 06:50:58", "47:59:59.123456", "47:59:59.1235", "47:59:59" },
&.{ "2023-11-30 06:50:58.000000", "2023-11-30 06:50:58.00", "2023-11-30 06:50:58", "47:59:59.000000", "47:59:59.0000", "47:59:59" },
&.{ "2023-11-30 00:00:00.000000", "2023-11-30 00:00:00.00", "2023-11-30 00:00:00", "00:00:00.000000", "00:00:00.0000", "00:00:00" },
};

try std.testing.expectEqualDeep(expected, table.rows);
Expand Down
42 changes: 40 additions & 2 deletions src/helper.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const PacketReader = protocol.packet_reader.PacketReader;
const packet_writer = protocol.packet_writer;
const ColumnDefinition41 = protocol.column_definition.ColumnDefinition41;
const DateTime = @import("./temporal.zig").DateTime;
const Duration = @import("./temporal.zig").Duration;

fn comptimeIntToUInt(
comptime Unsigned: type,
Expand All @@ -32,8 +33,8 @@ pub fn encodeBinaryParam(param: anytype, col_def: *const ColumnDefinition41, wri
const param_type_info = @typeInfo(@TypeOf(param));
const col_type: EnumFieldType = @enumFromInt(col_def.column_type);

switch (param_type_info) {
.Struct => {
switch (@TypeOf(param)) {
DateTime => {
switch (col_type) {
.MYSQL_TYPE_DATE,
.MYSQL_TYPE_DATETIME,
Expand All @@ -44,6 +45,18 @@ pub fn encodeBinaryParam(param: anytype, col_def: *const ColumnDefinition41, wri
else => {},
}
},
Duration => {
switch (col_type) {
.MYSQL_TYPE_TIME => {
return try encodeDuration(param, writer);
},
else => {},
}
},
else => {},
}

switch (param_type_info) {
.Null => return,
.Optional => {
if (param) |p| {
Expand Down Expand Up @@ -248,6 +261,31 @@ fn encodeDateTime(dt: DateTime, writer: anytype) !void {
}
}

// To save space the packet can be compressed:
// if day, hour, minutes, seconds and microseconds are all 0, length is 0 and no other field is sent.
// if microseconds is 0, length is 8 and micro_seconds is not sent.
// otherwise the length is 12
fn encodeDuration(d: Duration, writer: anytype) !void {
if (d.microseconds > 0) {
try packet_writer.writeUInt8(writer, 12);
try packet_writer.writeUInt8(writer, d.is_negative);
try packet_writer.writeUInt32(writer, d.days);
try packet_writer.writeUInt8(writer, d.hours);
try packet_writer.writeUInt8(writer, d.minutes);
try packet_writer.writeUInt8(writer, d.seconds);
try packet_writer.writeUInt32(writer, d.microseconds);
} else if (d.days > 0 or d.hours > 0 or d.minutes > 0 or d.seconds > 0) {
try packet_writer.writeUInt8(writer, 8);
try packet_writer.writeUInt8(writer, d.is_negative);
try packet_writer.writeUInt32(writer, d.days);
try packet_writer.writeUInt8(writer, d.hours);
try packet_writer.writeUInt8(writer, d.minutes);
try packet_writer.writeUInt8(writer, d.seconds);
} else {
try packet_writer.writeUInt8(writer, 0);
}
}

pub fn scanTextResultRow(raw: []const u8, dest: []?[]const u8) !void {
var packet_reader = PacketReader.initFromPayload(raw);
for (dest) |*d| {
Expand Down
9 changes: 5 additions & 4 deletions src/temporal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ pub const DateTime = struct {
// `Time` is ambigious and confusing, `Duration` was chosen as the name instead
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_binary_resultset.html#sect_protocol_binary_resultset_row_value_time
pub const Duration = struct {
is_negative: u8 = 0, // 1 if minus, 0 for plus
days: u32 = 0,
hour: u8 = 0,
minute: u8 = 0,
second: u8 = 0,
microsecond: u32 = 0,
hours: u8 = 0,
minutes: u8 = 0,
seconds: u8 = 0,
microseconds: u32 = 0,
};

0 comments on commit d4bedd7

Please sign in to comment.