mirror of
https://github.com/oven-sh/bun
synced 2026-02-11 11:29:02 +00:00
fix(sql) allow more than 64 columns (#17444)
This commit is contained in:
@@ -3259,10 +3259,31 @@ pub const PostgresSQLConnection = struct {
|
||||
u32,
|
||||
Flags,
|
||||
u8, // result_mode
|
||||
?[*]JSC.JSObject.ExternColumnIdentifier, // names
|
||||
u32, // names count
|
||||
) JSValue;
|
||||
|
||||
pub fn toJS(this: *Putter, globalObject: *JSC.JSGlobalObject, array: JSValue, structure: JSValue, flags: Flags, result_mode: PostgresSQLQueryResultMode) JSValue {
|
||||
return JSC__constructObjectFromDataCell(globalObject, array, structure, this.list.ptr, @truncate(this.fields.len), flags, @intFromEnum(result_mode));
|
||||
pub fn toJS(this: *Putter, globalObject: *JSC.JSGlobalObject, array: JSValue, structure: JSValue, flags: Flags, result_mode: PostgresSQLQueryResultMode, cached_structure: ?PostgresCachedStructure) JSValue {
|
||||
var names: ?[*]JSC.JSObject.ExternColumnIdentifier = null;
|
||||
var names_count: u32 = 0;
|
||||
if (cached_structure) |c| {
|
||||
if (c.fields) |f| {
|
||||
names = f.ptr;
|
||||
names_count = @truncate(f.len);
|
||||
}
|
||||
}
|
||||
|
||||
return JSC__constructObjectFromDataCell(
|
||||
globalObject,
|
||||
array,
|
||||
structure,
|
||||
this.list.ptr,
|
||||
@truncate(this.fields.len),
|
||||
flags,
|
||||
@intFromEnum(result_mode),
|
||||
names,
|
||||
names_count,
|
||||
);
|
||||
}
|
||||
|
||||
fn putImpl(this: *Putter, index: u32, optional_bytes: ?*Data, comptime is_raw: bool) !bool {
|
||||
@@ -3446,12 +3467,12 @@ pub const PostgresSQLConnection = struct {
|
||||
const request = this.current() orelse return error.ExpectedRequest;
|
||||
var statement = request.statement orelse return error.ExpectedStatement;
|
||||
var structure: JSValue = .undefined;
|
||||
var cached_structure: ?PostgresCachedStructure = null;
|
||||
// explict use switch without else so if new modes are added, we don't forget to check for duplicate fields
|
||||
switch (request.flags.result_mode) {
|
||||
.objects => {
|
||||
// check for duplicate fields
|
||||
statement.checkForDuplicateFields();
|
||||
structure = statement.structure(this.js_value, this.globalObject);
|
||||
cached_structure = statement.structure(this.js_value, this.globalObject);
|
||||
structure = cached_structure.?.jsValue() orelse .undefined;
|
||||
},
|
||||
.raw, .values => {
|
||||
// no need to check for duplicate fields or structure
|
||||
@@ -3466,17 +3487,17 @@ pub const PostgresSQLConnection = struct {
|
||||
.globalObject = this.globalObject,
|
||||
};
|
||||
|
||||
var stack_buf: [64]DataCell = undefined;
|
||||
var cells: []DataCell = stack_buf[0..@min(statement.fields.len, stack_buf.len)];
|
||||
var stack_buf: [70]DataCell = undefined;
|
||||
var cells: []DataCell = stack_buf[0..@min(statement.fields.len, JSC.JSC__JSObject__maxInlineCapacity)];
|
||||
var free_cells = false;
|
||||
defer {
|
||||
for (cells[0..putter.count]) |*cell| {
|
||||
cell.deinit();
|
||||
}
|
||||
if (free_cells) bun.default_allocator.free(cells);
|
||||
}
|
||||
|
||||
var free_cells = false;
|
||||
defer if (free_cells) bun.default_allocator.free(cells);
|
||||
if (statement.fields.len >= 64) {
|
||||
if (statement.fields.len >= JSC.JSC__JSObject__maxInlineCapacity) {
|
||||
cells = try bun.default_allocator.alloc(DataCell, statement.fields.len);
|
||||
free_cells = true;
|
||||
}
|
||||
@@ -3503,7 +3524,7 @@ pub const PostgresSQLConnection = struct {
|
||||
bun.assert(thisValue != .zero);
|
||||
const pending_value = PostgresSQLQuery.pendingValueGetCached(thisValue) orelse .zero;
|
||||
pending_value.ensureStillAlive();
|
||||
const result = putter.toJS(this.globalObject, pending_value, structure, statement.fields_flags, request.flags.result_mode);
|
||||
const result = putter.toJS(this.globalObject, pending_value, structure, statement.fields_flags, request.flags.result_mode, cached_structure);
|
||||
|
||||
if (pending_value == .zero) {
|
||||
PostgresSQLQuery.pendingValueSetCached(thisValue, this.globalObject, result);
|
||||
@@ -3906,8 +3927,39 @@ pub const PostgresSQLConnection = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const PostgresCachedStructure = struct {
|
||||
structure: JSC.Strong = .{},
|
||||
// only populated if more than JSC.JSC__JSObject__maxInlineCapacity fields otherwise the structure will contain all fields inlined
|
||||
fields: ?[]JSC.JSObject.ExternColumnIdentifier = null,
|
||||
|
||||
pub fn has(this: *@This()) bool {
|
||||
return this.structure.has() or this.fields != null;
|
||||
}
|
||||
|
||||
pub fn jsValue(this: *const @This()) ?JSC.JSValue {
|
||||
return this.structure.get();
|
||||
}
|
||||
|
||||
pub fn set(this: *@This(), globalObject: *JSC.JSGlobalObject, value: ?JSC.JSValue, fields: ?[]JSC.JSObject.ExternColumnIdentifier) void {
|
||||
if (value) |v| {
|
||||
this.structure.set(globalObject, v);
|
||||
}
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.structure.deinit();
|
||||
if (this.fields) |fields| {
|
||||
this.fields = null;
|
||||
for (fields) |*name| {
|
||||
name.deinit();
|
||||
}
|
||||
bun.default_allocator.free(fields);
|
||||
}
|
||||
}
|
||||
};
|
||||
pub const PostgresSQLStatement = struct {
|
||||
cached_structure: JSC.Strong = .{},
|
||||
cached_structure: PostgresCachedStructure = .{},
|
||||
ref_count: u32 = 1,
|
||||
fields: []protocol.FieldDescription = &[_]protocol.FieldDescription{},
|
||||
parameters: []const int4 = &[_]int4{},
|
||||
@@ -4024,43 +4076,57 @@ pub const PostgresSQLStatement = struct {
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
|
||||
pub fn structure(this: *PostgresSQLStatement, owner: JSValue, globalObject: *JSC.JSGlobalObject) JSValue {
|
||||
return this.cached_structure.get() orelse {
|
||||
const ids = bun.default_allocator.alloc(JSC.JSObject.ExternColumnIdentifier, this.fields.len) catch return .undefined;
|
||||
this.checkForDuplicateFields();
|
||||
defer {
|
||||
for (ids) |*name| {
|
||||
name.deinit();
|
||||
}
|
||||
bun.default_allocator.free(ids);
|
||||
}
|
||||
pub fn structure(this: *PostgresSQLStatement, owner: JSValue, globalObject: *JSC.JSGlobalObject) PostgresCachedStructure {
|
||||
if (this.cached_structure.has()) {
|
||||
return this.cached_structure;
|
||||
}
|
||||
this.checkForDuplicateFields();
|
||||
|
||||
for (this.fields, ids) |*field, *id| {
|
||||
id.tag = switch (field.name_or_index) {
|
||||
.name => 2,
|
||||
.index => 1,
|
||||
.duplicate => 0,
|
||||
};
|
||||
switch (field.name_or_index) {
|
||||
.name => |name| {
|
||||
id.value.name = String.createUTF8(name.slice());
|
||||
},
|
||||
.index => |index| {
|
||||
id.value.index = index;
|
||||
},
|
||||
.duplicate => {},
|
||||
}
|
||||
// lets avoid most allocations
|
||||
var stack_ids: [70]JSC.JSObject.ExternColumnIdentifier = undefined;
|
||||
// lets de duplicate the fields early
|
||||
var nonDuplicatedCount = this.fields.len;
|
||||
for (this.fields) |*field| {
|
||||
if (field.name_or_index == .duplicate) {
|
||||
nonDuplicatedCount -= 1;
|
||||
}
|
||||
const structure_ = JSC.JSObject.createStructure(
|
||||
}
|
||||
const ids = if (nonDuplicatedCount <= JSC.JSC__JSObject__maxInlineCapacity) stack_ids[0..nonDuplicatedCount] else bun.default_allocator.alloc(JSC.JSObject.ExternColumnIdentifier, nonDuplicatedCount) catch bun.outOfMemory();
|
||||
|
||||
var i: usize = 0;
|
||||
for (this.fields) |*field| {
|
||||
if (field.name_or_index == .duplicate) continue;
|
||||
|
||||
var id: *JSC.JSObject.ExternColumnIdentifier = &ids[i];
|
||||
switch (field.name_or_index) {
|
||||
.name => |name| {
|
||||
id.value.name = String.createAtomIfPossible(name.slice());
|
||||
},
|
||||
.index => |index| {
|
||||
id.value.index = index;
|
||||
},
|
||||
.duplicate => unreachable,
|
||||
}
|
||||
id.tag = switch (field.name_or_index) {
|
||||
.name => 2,
|
||||
.index => 1,
|
||||
.duplicate => 0,
|
||||
};
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (nonDuplicatedCount > JSC.JSC__JSObject__maxInlineCapacity) {
|
||||
this.cached_structure.set(globalObject, null, ids);
|
||||
} else {
|
||||
this.cached_structure.set(globalObject, JSC.JSObject.createStructure(
|
||||
globalObject,
|
||||
owner,
|
||||
@truncate(ids.len),
|
||||
ids.ptr,
|
||||
@bitCast(this.fields_flags),
|
||||
);
|
||||
this.cached_structure.set(globalObject, structure_);
|
||||
return structure_;
|
||||
};
|
||||
), null);
|
||||
}
|
||||
|
||||
return this.cached_structure;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user