diff --git a/src/sql/mysql.zig b/src/sql/mysql.zig index cb185a3a2d..cde0e33dec 100644 --- a/src/sql/mysql.zig +++ b/src/sql/mysql.zig @@ -741,7 +741,7 @@ pub const MySQLConnection = struct { this.ref(); const vm = this.globalObject.bunVM(); defer { - if (this.status == .connected and this.requests.items.len == 0 and this.write_buffer.remaining().len == 0) { + if (this.status == .connected and this.requests.readableLength() == 0 and this.write_buffer.remaining().len == 0) { // Don't keep the process alive when there's nothing to do. this.poll_ref.unref(vm); } else if (this.status == .connected) { @@ -823,12 +823,12 @@ pub const MySQLConnection = struct { // Read packet header const header = protocol.PacketHeader.decode(reader.peek()) orelse break; - try reader.skip(protocol.PACKET_HEADER_SIZE); + reader.skip(protocol.PACKET_HEADER_SIZE); // Ensure we have the full packet reader.ensureCapacity(header.length) catch |err| { if (err == error.ShortRead) { - try reader.skip(-@as(isize, @intCast(protocol.PACKET_HEADER_SIZE))); + reader.skip(-@as(isize, @intCast(protocol.PACKET_HEADER_SIZE))); } return err; @@ -848,7 +848,7 @@ pub const MySQLConnection = struct { }, } - try reader.skip(header.length); + reader.skip(header.length); } } @@ -908,7 +908,7 @@ pub const MySQLConnection = struct { pub fn handleAuth(this: *MySQLConnection, comptime Context: type, reader: protocol.NewReader(Context)) !void { const first_byte = try reader.int(u8); - try reader.skip(-1); + reader.skip(-1); switch (first_byte) { @intFromEnum(protocol.PacketType.OK) => { @@ -1017,7 +1017,7 @@ pub const MySQLConnection = struct { }; } - try response.writeInternal(Writer, this.writer()); + try response.write(this.writer()); this.flushData(); } @@ -1031,7 +1031,7 @@ pub const MySQLConnection = struct { .temporary = try auth_method.scramble(this.password, plugin_data, &scrambled_buf), }; - try response.writeInternal(Writer, this.writer()); + try response.write(this.writer()); this.flushData(); } @@ -1090,7 +1090,7 @@ pub const MySQLConnection = struct { return; } - this.connection.read_buffer.head += ucount; + this.connection.read_buffer.head += @intCast(ucount); } pub fn ensureCapacity(this: Reader, count: usize) bool { @@ -1132,7 +1132,7 @@ pub const MySQLConnection = struct { pub fn handlePreparedStatement(this: *MySQLConnection, comptime Context: type, reader: protocol.NewReader(Context)) !void { const first_byte = try reader.int(u8); - try reader.skip(-1); + reader.skip(-1); switch (first_byte) { @intFromEnum(protocol.PacketType.OK) => { @@ -1212,7 +1212,7 @@ pub const MySQLConnection = struct { pub fn handleResultSet(this: *MySQLConnection, comptime Context: type, reader: protocol.NewReader(Context)) !void { const first_byte = try reader.int(u8); - try reader.skip(-1); + reader.skip(-1); switch (first_byte) { @intFromEnum(protocol.PacketType.OK) => { @@ -1265,7 +1265,7 @@ pub const MySQLConnection = struct { // Start reading rows while (true) { const row_first_byte = try reader.byte(); - try reader.skip(-1); + reader.skip(-1); switch (row_first_byte) { @intFromEnum(protocol.PacketType.EOF) => { @@ -1300,12 +1300,12 @@ pub const MySQLConnection = struct { .binary = request.binary, }; defer row.deinit(allocator); - try row.decodeInternal(allocator, Context, reader); + try row.decode(allocator, reader); const pending_value = MySQLQuery.pendingValueGetCached(request.thisValue) orelse .zero; // Process row data - const row_value = row.toJS(request.statement.?.structure(request.thisValue, globalThis), pending_value, request.globalThis); + const row_value = row.toJS(request.statement.?.structure(request.thisValue, globalThis), pending_value, globalThis); if (globalThis.hasException()) { request.onJSError(globalThis.tryTakeException().?, globalThis); return error.JSError; @@ -1329,7 +1329,7 @@ pub const MySQLConnection = struct { .statement_id = statement.statement_id, }; - try close.writeInternal(Writer, this.writer()); + try close.write(this.writer()); this.flushData(); } @@ -1338,7 +1338,7 @@ pub const MySQLConnection = struct { .statement_id = statement.statement_id, }; - try reset.writeInternal(Writer, this.writer()); + try reset.write(this.writer()); this.flushData(); } }; @@ -1346,7 +1346,7 @@ pub const MySQLConnection = struct { pub const MySQLStatement = struct { cached_structure: JSC.Strong = .{}, ref_count: u32 = 1, - statement_id: u32, + statement_id: u32 = 0, params: []const types.FieldType = &[_]types.FieldType{}, columns: []const protocol.ColumnDefinition41 = &[_]protocol.ColumnDefinition41{}, signature: Signature, @@ -1508,7 +1508,7 @@ pub const MySQLQuery = struct { }; defer execute.deinit(); try this.bind(&execute, globalObject); - try execute.writeInternal(writer); + try execute.write(writer); this.status = .written; } @@ -1535,7 +1535,7 @@ pub const MySQLQuery = struct { // TODO: unsigned false, ); - defer value.deinit(); + defer value.deinit(bun.default_allocator); params[i] = try value.toData(param); i += 1; } @@ -1674,6 +1674,14 @@ pub const MySQLQuery = struct { return .undefined; } + pub fn doCancel(this: *MySQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + _ = callframe; + _ = globalObject; + _ = this; + + return .undefined; + } + pub fn doRun(this: *MySQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { var arguments_ = callframe.arguments_old(2); const arguments = arguments_.slice(); @@ -1739,6 +1747,7 @@ pub const MySQLQuery = struct { .signature = signature, .ref_count = 2, .status = .parsing, + .statement_id = 0, }; this.statement = stmt; entry.value_ptr.* = stmt; diff --git a/src/sql/mysql/mysql_protocol.zig b/src/sql/mysql/mysql_protocol.zig index 43a1cc72b1..e0eb462323 100644 --- a/src/sql/mysql/mysql_protocol.zig +++ b/src/sql/mysql/mysql_protocol.zig @@ -45,7 +45,7 @@ pub const StackReader = struct { this.message_start.* = this.offset.*; } - pub fn ensureLength(this: @This(), length: usize) bool { + pub fn ensureCapacity(this: @This(), length: usize) bool { return this.buffer.len >= (this.offset.* + length); } @@ -83,10 +83,6 @@ pub const StackReader = struct { this.offset.* += ucount; } - pub fn ensureCapacity(this: StackReader, count: usize) bool { - return this.buffer.len >= (this.offset.* + count); - } - pub fn read(this: StackReader, count: usize) anyerror!Data { const offset = this.offset.*; if (!this.ensureCapacity(count)) { @@ -126,17 +122,19 @@ pub fn NewWriterWrap( const offsetFn = offsetFn_; pub const Ctx = Context; + pub const is_wrapped = true; + pub const WrappedWriter = @This(); - pub inline fn write(this: @This(), data: []const u8) anyerror!void { + pub fn write(this: @This(), data: []const u8) anyerror!void { try writeFn(this.wrapped, data); } - pub inline fn offset(this: @This()) usize { + pub fn offset(this: @This()) usize { return offsetFn(this.wrapped); } - pub inline fn pwrite(this: @This(), data: []const u8, i: usize) anyerror!void { + pub fn pwrite(this: @This(), data: []const u8, i: usize) anyerror!void { try pwriteFn(this.wrapped, data, i); } @@ -195,24 +193,26 @@ pub fn NewReaderWrap( pub const Ctx = Context; - pub inline fn markMessageStart(this: @This()) void { + pub const is_wrapped = true; + + pub fn markMessageStart(this: @This()) void { markMessageStartFn(this.wrapped); } - pub inline fn read(this: @This(), count: usize) anyerror!Data { - return try readFn(this.wrapped, count); + pub fn read(this: @This(), count: usize) anyerror!Data { + return readFn(this.wrapped, count); } - pub fn skip(this: @This(), count: isize) anyerror!void { - skipFn(this.wrapped, count); + pub fn skip(this: @This(), count: anytype) void { + skipFn(this.wrapped, @as(isize, @intCast(count))); } pub fn peek(this: @This()) []const u8 { return peekFn(this.wrapped); } - pub inline fn readZ(this: @This()) anyerror!Data { - return try readZFn(this.wrapped); + pub fn readZ(this: @This()) anyerror!Data { + return readZFn(this.wrapped); } pub fn byte(this: @This()) !u8 { @@ -220,7 +220,7 @@ pub fn NewReaderWrap( return data.slice()[0]; } - pub inline fn ensureCapacity(this: @This(), count: usize) anyerror!void { + pub fn ensureCapacity(this: @This(), count: usize) anyerror!void { if (!ensureCapacityFn(this.wrapped, count)) { return error.ShortRead; } @@ -238,10 +238,18 @@ pub fn NewReaderWrap( } pub fn NewReader(comptime Context: type) type { - return NewReaderWrap(Context, Context.markMessageStart, Context.peek, Context.skip, Context.ensureLength, Context.read, Context.readZ); + if (@hasDecl(Context, "is_wrapped")) { + return Context; + } + + return NewReaderWrap(Context, Context.markMessageStart, Context.peek, Context.skip, Context.ensureCapacity, Context.read, Context.readZ); } pub fn NewWriter(comptime Context: type) type { + if (@hasDecl(Context, "is_wrapped")) { + return Context; + } + return NewWriterWrap(Context, Context.offset, Context.write, Context.pwrite); } @@ -249,7 +257,20 @@ fn decoderWrap(comptime Container: type, comptime decodeFn: anytype) type { return struct { pub fn decode(this: *Container, context: anytype) anyerror!void { const Context = @TypeOf(context); - try decodeFn(this, Context, NewReader(Context){ .wrapped = context }); + if (@hasDecl(Context, "is_wrapped")) { + try decodeFn(this, Context, context); + } else { + try decodeFn(this, Context, .{ .wrapped = context }); + } + } + + pub fn decodeAllocator(this: *Container, allocator: std.mem.Allocator, context: anytype) anyerror!void { + const Context = @TypeOf(context); + if (@hasDecl(Context, "is_wrapped")) { + try decodeFn(this, allocator, Context, context); + } else { + try decodeFn(this, allocator, Context, .{ .wrapped = context }); + } } }; } @@ -258,7 +279,11 @@ fn writeWrap(comptime Container: type, comptime writeFn: anytype) type { return struct { pub fn write(this: *Container, context: anytype) anyerror!void { const Context = @TypeOf(context); - try writeFn(this, Context, NewWriter(Context){ .wrapped = context }); + if (@hasDecl(Context, "is_wrapped")) { + try writeFn(this, Context, context); + } else { + try writeFn(this, Context, .{ .wrapped = context }); + } } }; } @@ -413,7 +438,7 @@ pub const HandshakeV10 = struct { } // Skip reserved bytes - try reader.skip(10); + reader.skip(10); // Auth plugin data part 2 const remaining_auth_len = @max(13, auth_plugin_data_len - 8); @@ -536,7 +561,7 @@ pub const OKPacket = struct { // Affected rows (length encoded integer) if (decodeLengthInt(reader.peek())) |result| { this.affected_rows = result.value; - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); } else { return error.InvalidOKPacket; } @@ -544,7 +569,7 @@ pub const OKPacket = struct { // Last insert ID (length encoded integer) if (decodeLengthInt(reader.peek())) |result| { this.last_insert_id = result.value; - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); } else { return error.InvalidOKPacket; } @@ -565,7 +590,7 @@ pub const OKPacket = struct { if (decodeLengthInt(reader.peek())) |result| { const state_data = try reader.read(@intCast(result.value)); this.session_state_changes = state_data; - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); } } } @@ -602,7 +627,7 @@ pub const ErrorPacket = struct { this.sql_state = sql_state_data.slice()[0..5].*; } else { // No SQL state, rewind one byte - try reader.skip(-1); + reader.skip(-1); } // Read the error message (rest of packet) @@ -848,32 +873,32 @@ pub const ColumnDefinition41 = struct { pub fn decodeInternal(this: *ColumnDefinition41, comptime Context: type, reader: NewReader(Context)) !void { // Length encoded strings if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); this.catalog = try reader.read(@intCast(result.value)); } else return error.InvalidColumnDefinition; if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); this.schema = try reader.read(@intCast(result.value)); } else return error.InvalidColumnDefinition; if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); this.table = try reader.read(@intCast(result.value)); } else return error.InvalidColumnDefinition; if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); this.org_table = try reader.read(@intCast(result.value)); } else return error.InvalidColumnDefinition; if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); this.name = try reader.read(@intCast(result.value)); } else return error.InvalidColumnDefinition; if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); this.org_name = try reader.read(@intCast(result.value)); } else return error.InvalidColumnDefinition; @@ -888,7 +913,7 @@ pub const ColumnDefinition41 = struct { this.decimals = try reader.int(u8); // Skip filler - try reader.skip(2); + reader.skip(2); } pub const decode = decoderWrap(ColumnDefinition41, decodeInternal).decode; @@ -898,7 +923,7 @@ fn decodeBinaryValue(field_type: types.FieldType, unsigned: bool, comptime Conte return switch (field_type) { .MYSQL_TYPE_TINY => blk: { const val = try reader.byte(); - break :blk Value{ .bool = val[0] != 0 }; + break :blk Value{ .bool = val != 0 }; }, .MYSQL_TYPE_SHORT => if (unsigned) Value{ .ushort = try reader.int(u16) } @@ -915,38 +940,42 @@ fn decodeBinaryValue(field_type: types.FieldType, unsigned: bool, comptime Conte .MYSQL_TYPE_FLOAT => Value{ .float = @bitCast(try reader.int(u32)) }, .MYSQL_TYPE_DOUBLE => Value{ .double = @bitCast(try reader.int(u64)) }, .MYSQL_TYPE_TIME => switch (try reader.byte()) { - 0 => Value{ .null = .{} }, - 8, 12 => |l| Value{ .time = try Value.Time.fromBinary(reader.read(l)) }, + 0 => Value{ .null = {} }, + 8, 12 => |l| Value{ .time = try Value.Time.fromData(&try reader.read(l)) }, else => return error.InvalidBinaryValue, }, .MYSQL_TYPE_DATE => switch (try reader.byte()) { - 0 => Value{ .null = .{} }, - 4 => Value{ .date = try Value.DateTime.fromBinary(reader.read(4)) }, + 0 => Value{ .null = {} }, + 4 => Value{ .date = try Value.DateTime.fromData(&try reader.read(4)) }, else => error.InvalidBinaryValue, }, .MYSQL_TYPE_DATETIME => switch (try reader.byte()) { - 0 => Value{ .null = .{} }, - 11, 7, 4 => |l| Value{ .date = try Value.DateTime.fromBinary(reader.read(l)) }, + 0 => Value{ .null = {} }, + 11, 7, 4 => |l| Value{ .date = try Value.DateTime.fromData(&try reader.read(l)) }, else => error.InvalidBinaryValue, }, .MYSQL_TYPE_TIMESTAMP => switch (try reader.byte()) { - 0 => Value{ .null = .{} }, - 4, 7 => |l| Value{ .timestamp = try Value.Timestamp.fromBinary(reader.read(l)) }, + 0 => Value{ .null = {} }, + 4, 7 => |l| Value{ .timestamp = try Value.Timestamp.fromData(&try reader.read(l)) }, else => error.InvalidBinaryValue, }, + .MYSQL_TYPE_STRING, .MYSQL_TYPE_VARCHAR, .MYSQL_TYPE_VAR_STRING => blk: { + if (decodeLengthInt(reader.peek())) |result| { + reader.skip(result.bytes_read); + const val = try reader.read(@intCast(result.value)); + break :blk .{ .string_data = val }; + } else return error.InvalidBinaryValue; + }, .MYSQL_TYPE_TINY_BLOB, .MYSQL_TYPE_MEDIUM_BLOB, .MYSQL_TYPE_LONG_BLOB, .MYSQL_TYPE_BLOB, - .MYSQL_TYPE_STRING, - .MYSQL_TYPE_VARCHAR, - .MYSQL_TYPE_VAR_STRING, .MYSQL_TYPE_JSON, => blk: { if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); const val = try reader.read(@intCast(result.value)); - break :blk .{ .bytes = val }; + break :blk .{ .bytes_data = val }; } else return error.InvalidBinaryValue; }, else => return error.UnsupportedColumnType, @@ -955,14 +984,14 @@ fn decodeBinaryValue(field_type: types.FieldType, unsigned: bool, comptime Conte // Result set header packet pub const ResultSetHeader = struct { - field_count: u64, + field_count: u64 = 0, extra: ?u64 = null, pub fn decodeInternal(this: *ResultSetHeader, comptime Context: type, reader: NewReader(Context)) !void { // Field count (length encoded integer) if (decodeLengthInt(reader.peek())) |result| { this.field_count = result.value; - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); } else { return error.InvalidResultSetHeader; } @@ -971,7 +1000,7 @@ pub const ResultSetHeader = struct { if (reader.peek().len > 0) { if (decodeLengthInt(reader.peek())) |result| { this.extra = result.value; - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); } } } @@ -1268,14 +1297,14 @@ pub const Auth = struct { // Result set packet types pub const ResultSet = struct { pub const Header = struct { - field_count: u64, + field_count: u64 = 0, extra: ?u64 = null, pub fn decodeInternal(this: *Header, comptime Context: type, reader: NewReader(Context)) !void { // Field count (length encoded integer) if (decodeLengthInt(reader.peek())) |result| { this.field_count = result.value; - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); } else { return error.InvalidResultSetHeader; } @@ -1284,7 +1313,7 @@ pub const ResultSet = struct { if (reader.peek().len > 0) { if (decodeLengthInt(reader.peek())) |result| { this.extra = result.value; - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); } } } @@ -1299,7 +1328,7 @@ pub const ResultSet = struct { extern fn MySQL__toJSFromRow(*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue, *anyopaque, usize) JSC.JSValue; pub fn toJS(this: *Row, structure_value: JSValue, array_value: JSValue, globalObject: *JSC.JSGlobalObject) JSValue { - return MySQL__toJSFromRow(globalObject, structure_value, array_value, this.values, this.columns.len); + return MySQL__toJSFromRow(globalObject, structure_value, array_value, this.values.ptr, this.columns.len); } pub fn deinit(this: *Row, allocator: std.mem.Allocator) void { @@ -1330,12 +1359,12 @@ pub const ResultSet = struct { for (values) |*value| { if (decodeLengthInt(reader.peek())) |result| { - try reader.skip(result.bytes_read); + reader.skip(result.bytes_read); if (result.value == 0xfb) { // NULL value value.* = .{ .null = {} }; } else { value.* = .{ - .string = try reader.read(@intCast(result.value)), + .string_data = try reader.read(@intCast(result.value)), }; } } else { @@ -1373,18 +1402,18 @@ pub const ResultSet = struct { const is_null = (null_bitmap.slice()[byte_pos] & (@as(u8, 1) << bit_pos)) != 0; if (is_null) { - value.* = .{ .empty = {} }; + value.* = .{ .null = {} }; continue; } const column = this.columns[i]; - value.* = try decodeBinaryValue(column.column_type, Context, reader); + value.* = try decodeBinaryValue(column.column_type, column.flags.UNSIGNED, Context, reader); } this.values = values; } - pub const decode = decoderWrap(Row, decodeInternal).decode; + pub const decode = decoderWrap(Row, decodeInternal).decodeAllocator; }; }; @@ -1392,7 +1421,7 @@ pub const ResultSet = struct { pub const PreparedStatement = struct { pub const Prepare = struct { command: CommandType = .COM_STMT_PREPARE, - query: Data, + query: Data = .{ .empty = {} }, pub fn deinit(this: *Prepare) void { this.query.deinit(); @@ -1441,7 +1470,7 @@ pub const PreparedStatement = struct { /// Whether to send parameter types. Set to true for first execution, false for subsequent executions new_params_bind_flag: bool = true, /// Parameter values to bind to the prepared statement - params: []const Data = &[_]Data{}, + params: []Data = &[_]Data{}, /// Types of each parameter in the prepared statement param_types: []const types.FieldType = &[_]types.FieldType{}, @@ -1504,7 +1533,7 @@ pub const PreparedStatement = struct { pub const Close = struct { command: CommandType = .COM_STMT_CLOSE, - statement_id: u32, + statement_id: u32 = 0, pub fn writeInternal(this: *const Close, comptime Context: type, writer: NewWriter(Context)) !void { try writer.int1(@intFromEnum(this.command)); @@ -1516,7 +1545,7 @@ pub const PreparedStatement = struct { pub const Reset = struct { command: CommandType = .COM_STMT_RESET, - statement_id: u32, + statement_id: u32 = 0, pub fn writeInternal(this: *const Reset, comptime Context: type, writer: NewWriter(Context)) !void { try writer.int1(@intFromEnum(this.command)); diff --git a/src/sql/mysql/mysql_types.zig b/src/sql/mysql/mysql_types.zig index 6c9022b76e..533069f1fb 100644 --- a/src/sql/mysql/mysql_types.zig +++ b/src/sql/mysql/mysql_types.zig @@ -183,7 +183,9 @@ pub const Value = union(enum) { double: f64, string: JSC.ZigString.Slice, + string_data: Data, bytes: JSC.ZigString.Slice, + bytes_data: Data, date: DateTime, timestamp: Timestamp, time: Time, @@ -191,7 +193,8 @@ pub const Value = union(enum) { pub fn deinit(this: *Value, allocator: std.mem.Allocator) void { switch (this.*) { - inline .string, .bytes => |*slice| slice.deinit(allocator), + inline .string, .bytes => |*slice| slice.deinit(), + inline .string_data, .bytes_data => |*data| data.deinit(), .decimal => |*decimal| decimal.deinit(allocator), else => {}, } @@ -219,6 +222,7 @@ pub const Value = union(enum) { stream.pos = d.toBinary(field_type, &buffer); }, .decimal => |dec| return try dec.toBinary(field_type), + .string_data, .bytes_data => |data| return data, .string, .bytes => |slice| return if (slice.len > 0) Data{ .temporary = slice.slice() } else @@ -285,6 +289,10 @@ pub const Value = union(enum) { seconds: u32, microseconds: u24, + pub fn fromData(data: *const Data) !Timestamp { + return fromBinary(data.slice()); + } + pub fn fromBinary(val: []const u8) Timestamp { return .{ // Bytes 0-3: [seconds] (32-bit little-endian unsigned integer) @@ -348,6 +356,10 @@ pub const Value = union(enum) { second: u8 = 0, microsecond: u32 = 0, + pub fn fromData(data: *const Data) !DateTime { + return fromBinary(data.slice()); + } + pub fn fromBinaryDate(val: []const u8) DateTime { return .{ .year = std.mem.readInt(u16, val[0..2], .little), @@ -532,6 +544,10 @@ pub const Value = union(enum) { return total_ms; } + pub fn fromData(data: *const Data) !Time { + return fromBinary(data.slice()); + } + pub fn fromBinary(val: []const u8) Time { if (val.len == 0) { return Time{}; @@ -542,14 +558,14 @@ pub const Value = union(enum) { if (length >= 8) { time.negative = val[1] != 0; - time.days = std.mem.readInt(.little, val[2..6]); + time.days = std.mem.readInt(u32, val[2..6], .little); time.hours = val[6]; time.minutes = val[7]; time.seconds = val[8]; } if (length > 8) { - time.microseconds = std.mem.readInt(.little, val[9..13]); + time.microseconds = std.mem.readInt(u32, val[9..13], .little); } return time; @@ -584,7 +600,7 @@ pub const Value = union(enum) { return 9; } else { buffer[0] = 12; // length - std.mem.writeInt(u32, buffer[9..], this.microseconds, .little); + std.mem.writeInt(u32, buffer[9..][0..4], this.microseconds, .little); return 12; } }, @@ -629,17 +645,27 @@ pub const Value = union(enum) { _ = field_type; // autofix bun.todoPanic(@src(), "Decimal.toBinary not implemented", .{}); } + + pub fn fromData(data: *const Data) !Decimal { + return fromBinary(data.slice()); + } + + pub fn fromBinary(val: []const u8) Decimal { + _ = val; // autofix + // TODO: handle overflow + bun.todoPanic(@src(), "Decimal.fromBinary not implemented", .{}); + } }; pub fn toJS(this: *const Value, globalObject: *JSC.JSGlobalObject) JSValue { return switch (this.*) { .null => JSValue.jsNull(), .bool => |b| JSValue.jsBoolean(b), - .string => |*str| { + inline .string, .string_data => |*str| { var out = bun.String.createUTF8(str.slice()); return out.transferToJS(globalObject); }, - .bytes => JSC.ArrayBuffer.createBuffer(globalObject, this.bytes.slice()), + inline .bytes, .bytes_data => |*data| JSC.ArrayBuffer.createBuffer(globalObject, data.slice()), inline .long, .int, .float, .double, .short, .ushort, .uint, .ulong => |t| JSValue.jsNumber(t), inline .timestamp, .date, .time, .decimal => |*d| d.toJS(globalObject), };