lots of code

This commit is contained in:
Jarred Sumner
2024-11-25 02:12:44 -08:00
parent b1f8cd623f
commit 028e98e18d
7 changed files with 138 additions and 54 deletions

View File

@@ -2,7 +2,7 @@ import { define } from "../../codegen/class-definitions";
export default [
define({
name: "MySQLSQLConnection",
name: "MySQLConnection",
construct: true,
finalize: true,
hasPendingActivity: true,
@@ -38,7 +38,7 @@ export default [
},
}),
define({
name: "MySQLSQLQuery",
name: "MySQLQuery",
construct: true,
finalize: true,
configurable: false,

View File

@@ -4168,6 +4168,18 @@ pub const JSValue = enum(i64) {
return cppFn("coerceToDouble", .{ this, globalObject });
}
pub fn coerceToDoubleCheckingErrors(
this: JSValue,
globalObject: *JSC.JSGlobalObject,
) f64 {
const num = this.coerceToDouble(globalObject);
if (globalObject.hasException()) {
return error.JSError;
}
return num;
}
pub fn coerce(this: JSValue, comptime T: type, globalThis: *JSC.JSGlobalObject) T {
return switch (T) {
ZigString => this.getZigString(globalThis),

View File

@@ -24,6 +24,7 @@ stdin_store: ?*Blob.Store = null,
stdout_store: ?*Blob.Store = null,
postgresql_context: JSC.Postgres.PostgresSQLContext = .{},
mysql_context: JSC.MySQL.MySQLContext = .{},
entropy_cache: ?*EntropyCache = null,

View File

@@ -20,6 +20,8 @@ pub const int2 = u16;
pub const int3 = u24;
pub const int4 = u32;
pub const int8 = u64;
const Value = types.Value;
const FieldType = types.FieldType;
pub const MySQLInt8 = int1;
pub const MySQLInt16 = int2;
@@ -815,14 +817,14 @@ pub const MySQLConnection = struct {
// Read parameter definitions if any
if (ok.num_params > 0) {
var params = try bun.default_allocator.alloc(types.FieldType, ok.num_params);
const params = try bun.default_allocator.alloc(types.FieldType, ok.num_params);
errdefer bun.default_allocator.free(params);
for (params) |*param| {
var column = protocol.ColumnDefinition41{};
defer column.deinit();
try column.decode(Context, reader);
param.* = column.column_type;
column.deinit();
}
statement.params = params;
@@ -830,22 +832,32 @@ pub const MySQLConnection = struct {
// Read column definitions if any
if (ok.num_columns > 0) {
var columns = try bun.default_allocator.alloc(protocol.ColumnDefinition41, ok.num_columns);
const columns = try bun.default_allocator.alloc(protocol.ColumnDefinition41, ok.num_columns);
var consumed: u32 = 0;
errdefer {
for (columns) |*column| {
for (columns[0..consumed]) |*column| {
column.deinit();
}
bun.default_allocator.free(columns);
}
for (0..ok.num_columns) |i| {
try columns[i].decode(Context, reader);
for (columns) |*column| {
try column.decode(Context, reader);
consumed += 1;
}
statement.columns = columns;
}
try this.executeStatement(statement, request, this.globalObject);
var execute = protocol.PreparedStatement.Execute{
.statement_id = statement.statement_id,
.param_types = statement.params,
.iteration_count = 1,
};
defer execute.deinit();
try request.bind(&execute, this.globalObject);
try execute.writeInternal(Context, this.writer());
this.flushData();
}
},
@@ -854,7 +866,7 @@ pub const MySQLConnection = struct {
try err.decode(Context, reader);
defer err.deinit();
if (this.requests.popOrNull()) |request| {
if (this.requests.readItem()) |request| {
if (request.statement) |statement| {
statement.status = .failed;
statement.error_response = err;
@@ -903,14 +915,14 @@ pub const MySQLConnection = struct {
var header = protocol.ResultSetHeader{};
try header.decode(Context, reader);
if (this.requests.readableLength() > 0) {
const request = this.requests.peekItem(0);
// Read column definitions
const columns = try bun.default_allocator.alloc(protocol.ColumnDefinition41, header.field_count);
var columns_read: u32 = 0;
errdefer {
for (columns) |*column| {
for (columns[0..columns_read]) |*column| {
column.deinit();
}
bun.default_allocator.free(columns);
@@ -918,8 +930,10 @@ pub const MySQLConnection = struct {
for (columns) |*column| {
try column.decode(Context, reader);
columns_read += 1;
}
const globalThis = this.globalObject;
const globalThis = this.globalObject;
// Start reading rows
while (true) {
const row_first_byte = try reader.byte();
@@ -933,9 +947,9 @@ const globalThis = this.globalObject;
// Update status flags and finish
this.status_flags = eof.status_flags;
this.is_ready_for_query = true;
this.requests.discard(1);
request.onSuccess(0, 0, this.globalObject);
request.onSuccess(this.globalObject);
break;
},
@@ -950,50 +964,36 @@ const globalThis = this.globalObject;
else => {
var stack_fallback = std.heap.stackFallback(4096, bun.default_allocator);
const allocator = stack_fallback.get();
// Read row data
var row = protocol.ResultSet.Row{
.columns = columns,
.binary = request.binary,
};
const allocator = stack_fallback.get();
try row.decodeInternal(allocator, Context, reader);
defer row.deinit(allocator);
try row.decodeInternal(allocator, Context, reader);
const pending_value = MySQLQuery.pendingValueGetCached(request.thisValue) orelse .zero;
// Process row data
// Note: You'll need to implement row processing logic
// based on your application's needs
row.toJS(request.statement.?.structure(request.thisValue, globalThis), request. globalThis);
const row_value = row.toJS(request.statement.?.structure(request.thisValue, globalThis), pending_value, request.globalThis);
if (globalThis.hasException()) {
request.onJSError(globalThis.tryTakeException().?, globalThis);
return error.JSError;
}
if (pending_value == .zero) {
MySQLQuery.pendingValueSetCached(request.thisValue, globalThis, row_value);
}
},
}
}
// Clean up columns
for (columns) |*column| {
column.deinit();
}
bun.default_allocator.free(columns);
}
},
}
}
pub fn executeStatement(this: *MySQLConnection, statement: *MySQLStatement, globalObject: *JSC.JSGlobalObject) !void {
var execute = protocol.PreparedStatement.Execute{
.statement_id = statement.statement_id,
.params = values,
.param_types = statement.params,
.iteration_count = 1,
};
defer execute.deinit();
const array
try execute.writeInternal()
this.flushData();
}
pub fn closeStatement(this: *MySQLConnection, statement: *MySQLStatement) !void {
var close = protocol.PreparedStatement.Close{
.statement_id = statement.statement_id,
@@ -1170,6 +1170,31 @@ pub const MySQLQuery = struct {
});
}
pub fn bind(this: *MySQLQuery, execute: *protocol.PreparedStatement.Execute, globalObject: *JSC.JSGlobalObject) !void {
const binding_value = MySQLQuery.bindingGetCached(this.thisValue) orelse .zero;
const columns_value = MySQLQuery.columnsGetCached(this.thisValue) orelse .zero;
var iter = QueryBindingIterator.init(binding_value, columns_value, globalObject);
var i: u32 = 0;
var params = try bun.default_allocator.alloc(Data, execute.params.len);
errdefer {
for (params[0..i]) |*param| {
param.deinit();
}
bun.default_allocator.free(params);
}
while (iter.next()) |js_value| {
const param = execute.param_types[i];
const value = try Value.fromJS(js_value, globalObject, param, bun.default_allocator);
params[i] = try value.toData(param);
i += 1;
}
this.status = .binding;
execute.params = params;
}
pub fn onError(this: *@This(), err: protocol.ErrorPacket, globalObject: *JSC.JSGlobalObject) void {
this.status = .fail;
defer {
@@ -1192,7 +1217,29 @@ pub const MySQLQuery = struct {
globalObject.queueMicrotask(function, &[_]JSValue{ targetValue, err.toJS(globalObject) });
}
pub fn onSuccess(this: *@This(), affected_rows: u64, last_insert_id: u64, globalObject: *JSC.JSGlobalObject) void {
pub fn onJSError(this: *@This(), exception: JSC.JSValue, globalObject: *JSC.JSGlobalObject) void {
this.status = .fail;
defer {
// Clean up statement reference on error
if (this.statement) |statement| {
statement.deref();
this.statement = null;
}
this.deref();
}
const thisValue = this.thisValue;
const targetValue = this.target.trySwap() orelse JSValue.zero;
if (thisValue == .zero or targetValue == .zero) {
return;
}
var vm = JSC.VirtualMachine.get();
const function = vm.rareData().mysql_context.onQueryRejectFn.get().?;
globalObject.queueMicrotask(function, &[_]JSValue{ targetValue, exception.toError().? });
}
pub fn onSuccess(this: *@This(), globalObject: *JSC.JSGlobalObject) void {
this.status = .success;
defer this.deref();
@@ -1208,8 +1255,8 @@ pub const MySQLQuery = struct {
event_loop.runCallback(function, globalObject, thisValue, &.{
targetValue,
this.pending_value.trySwap() orelse .undefined,
JSValue.jsNumber(@floatFromInt(affected_rows)),
JSValue.jsNumber(@floatFromInt(last_insert_id)),
JSValue.jsNumber(0),
JSValue.jsNumber(0),
});
}

View File

@@ -1486,6 +1486,8 @@ pub const PreparedStatement = struct {
for (this.params, 0..) |param, i| {
if (param == .empty) {
null_bitmap[i >> 3] |= @as(u8, 1) << @as(u3, @truncate(i & 7));
} else {
bun.assert(param.slice().len > 0);
}
}
@@ -1503,8 +1505,8 @@ pub const PreparedStatement = struct {
}
// Write parameter values
for (this.params, this.param_types) |param, param_type| {
if (param == .empty) continue;
for (this.params, this.param_types) |*param, param_type| {
if (param.* == .empty) continue;
const value = param.slice();
if (param_type.isBinaryFormatSupported()) {

View File

@@ -1617,7 +1617,7 @@ pub const PostgresSQLConnection = struct {
pub const Value = extern union {
null: u8,
string: bun.WTF.StringImpl,
string: ?bun.WTF.StringImpl,
float8: f64,
int4: i32,
int8: i64,

View File

@@ -1,6 +1,7 @@
const JSC = bun.JSC;
const bun = @import("root").bun;
const JSValue = JSC.JSValue;
const std = @import("std");
pub const QueryBindingIterator = union(enum) {
array: JSC.JSArrayIterator,
@@ -124,13 +125,29 @@ pub const QueryBindingIterator = union(enum) {
pub const Data = union(enum) {
owned: bun.ByteList,
temporary: []const u8,
inline_storage: std.BoundedArray(u8, 15),
empty: void,
pub fn create(possibly_inline_bytes: []const u8, allocator: std.mem.Allocator) !Data {
if (possibly_inline_bytes.len == 0) {
return .{ .empty = {} };
}
if (possibly_inline_bytes.len <= 15) {
var inline_storage = std.BoundedArray(u8, 15){};
@memcpy(inline_storage.buffer[0..possibly_inline_bytes.len], possibly_inline_bytes);
inline_storage.len = @truncate(possibly_inline_bytes.len);
return .{ .inline_storage = inline_storage };
}
return .{ .owned = bun.ByteList.init(try allocator.dupe(u8, possibly_inline_bytes)) };
}
pub fn toOwned(this: @This()) !bun.ByteList {
return switch (this) {
.owned => this.owned,
.temporary => bun.ByteList.init(try bun.default_allocator.dupe(u8, this.temporary)),
.empty => bun.ByteList.init(&.{}),
.inline_storage => bun.ByteList.init(try bun.default_allocator.dupe(u8, this.inline_storage.slice())),
};
}
@@ -139,6 +156,7 @@ pub const Data = union(enum) {
.owned => this.owned.deinitWithAllocator(bun.default_allocator),
.temporary => {},
.empty => {},
.inline_storage => {},
}
}
@@ -155,30 +173,34 @@ pub const Data = union(enum) {
},
.temporary => {},
.empty => {},
.inline_storage => {},
}
}
pub fn slice(this: @This()) []const u8 {
return switch (this) {
pub fn slice(this: *const @This()) []const u8 {
return switch (this.*) {
.owned => this.owned.slice(),
.temporary => this.temporary,
.empty => "",
.inline_storage => this.inline_storage.slice(),
};
}
pub fn substring(this: @This(), start_index: usize, end_index: usize) Data {
return switch (this) {
pub fn substring(this: *const @This(), start_index: usize, end_index: usize) Data {
return switch (this.*) {
.owned => .{ .temporary = this.owned.slice()[start_index..end_index] },
.temporary => .{ .temporary = this.temporary[start_index..end_index] },
.empty => .{ .empty = {} },
.inline_storage => .{ .temporary = this.inline_storage.slice()[start_index..end_index] },
};
}
pub fn sliceZ(this: @This()) [:0]const u8 {
return switch (this) {
pub fn sliceZ(this: *const @This()) [:0]const u8 {
return switch (this.*) {
.owned => this.owned.slice()[0..this.owned.len :0],
.temporary => this.temporary[0..this.temporary.len :0],
.empty => "",
.inline_storage => this.inline_storage.slice()[0..this.inline_storage.len :0],
};
}
};