Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/bun.js/VirtualMachine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -830,13 +830,11 @@ pub fn onExit(this: *VirtualMachine) void {
}
}

extern fn Zig__GlobalObject__destructOnExit(*JSGlobalObject) void;

pub fn globalExit(this: *VirtualMachine) noreturn {
bun.assert(this.isShuttingDown());
if (this.shouldDestructMainThreadOnExit()) {
if (this.eventLoop().forever_timer) |t| t.deinit(true);
Zig__GlobalObject__destructOnExit(this.global);
bun.cpp.Zig__GlobalObject__destructOnExit(this.global);
this.transpiler.deinit();
this.gc_controller.deinit();
this.deinit();
Expand Down
3 changes: 1 addition & 2 deletions src/bun.js/bindings/JSObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pub const JSObject = opaque {

extern fn JSC__JSObject__getIndex(this: JSValue, globalThis: *JSGlobalObject, i: u32) JSValue;
extern fn Bun__JSObject__getCodePropertyVMInquiry(global: *JSGlobalObject, obj: *JSObject) JSValue;
extern fn JSC__createStructure(global: *jsc.JSGlobalObject, owner: *jsc.JSCell, length: u32, names: [*]ExternColumnIdentifier) jsc.JSValue;
extern fn JSC__JSObject__create(global_object: *JSGlobalObject, length: usize, ctx: *anyopaque, initializer: InitializeCallback) JSValue;

pub fn toJS(obj: *JSObject) JSValue {
Expand Down Expand Up @@ -117,7 +116,7 @@ pub const JSObject = opaque {

pub fn createStructure(global: *JSGlobalObject, owner: jsc.JSValue, length: u32, names: [*]ExternColumnIdentifier) JSValue {
jsc.markBinding(@src());
return JSC__createStructure(global, owner.asCell(), length, names);
return bun.cpp.JSC__createStructure(global, owner.asCell(), names, length);
}

const InitializeCallback = *const fn (ctx: *anyopaque, obj: *JSObject, global: *JSGlobalObject) callconv(.C) void;
Expand Down
16 changes: 8 additions & 8 deletions src/bun.js/bindings/SQLClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ typedef struct TypedArrayDataCell {

typedef union DataCellValue {
uint8_t null_value;
WTF::StringImpl* string;
BunString string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Verify proper memory management for BunString fields.

The change from WTF::StringImpl* to BunString for string and json fields is a good improvement for memory safety. However, ensure that:

  1. BunString instances are properly initialized (with .dead sentinel when empty)
  2. The cleanup/deref logic is consistently applied when DataCell instances are destroyed

Also applies to: 72-72


🏁 Script executed:

#!/bin/bash
# Search for DataCell cleanup/destruction patterns to verify proper BunString handling
ast-grep --pattern 'DataCell::~DataCell()' 
rg -n "DataCell.*deinit|DataCell.*destroy|DataCell.*cleanup" --type cpp --type zig

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Locate DataCell definition and any destructor
rg -nP "(class|struct)\s+DataCell" -C5 --type=cpp
rg -n "DataCell::~DataCell" -C2 --type=cpp

# Show DataCell usage in SQLClient.cpp
rg -n "DataCell" -C3 src/bun.js/bindings/SQLClient.cpp

# Locate BunString definition to verify its RAII behavior
rg -nP "(class|struct)\s+BunString" -C5

Length of output: 9803


Implement DataCell destructor for BunString cleanup

  • Add DataCell::~DataCell() in src/bun.js/bindings/SQLClient.cpp to deinit value.string and value.json when tag == DataCellTag::String or DataCellTag::Json.
  • Ensure default-constructed BunString is initialized with Dead tag.
🤖 Prompt for AI Agents
In src/bun.js/bindings/SQLClient.cpp around line 63, the DataCell currently
contains a BunString but lacks a destructor to clean it up; add a
DataCell::~DataCell() that checks the DataCell::tag and when tag ==
DataCellTag::String or DataCellTag::Json calls the proper deinit/free on
value.string and value.json respectively, then resets tag to a safe state;
additionally ensure the BunString default constructor sets its internal
tag/state to Dead so uninitialized instances are safe to destruct.

double number;
int32_t integer;
int64_t bigint;
Expand All @@ -69,7 +69,7 @@ typedef union DataCellValue {
double date;
double date_with_time_zone;
size_t bytea[2];
WTF::StringImpl* json;
BunString json;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait why make this change? we always want it to be a WTF::StringImpl?

DataCellArray array;
TypedArrayDataCell typed_array;
DataCellRaw raw;
Expand Down Expand Up @@ -146,8 +146,8 @@ static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCel
return uint8Array;
}
case DataCellTag::String: {
if (cell.value.string) {
return jsString(vm, WTF::String(cell.value.string));
if (cell.value.string.tag != BunStringTag::Dead) {
return jsString(vm, cell.value.string.transferToWTFString());
}
return jsEmptyString(vm);
}
Expand Down Expand Up @@ -186,8 +186,8 @@ static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCel
return uint8Array;
}
case DataCellTag::Json: {
if (cell.value.json) {
auto str = WTF::String(cell.value.json);
if (cell.value.json.tag != BunStringTag::Dead) {
auto str = cell.value.json.transferToWTFString();
JSC::JSValue json = JSC::JSONParse(globalObject, str);
RETURN_IF_EXCEPTION(scope, {});
return json;
Expand Down Expand Up @@ -430,7 +430,7 @@ extern "C" EncodedJSValue JSC__constructObjectFromDataCell(
return JSValue::encode(toJS(array, structure, cells, count, globalObject, Bun::BunStructureFlags(flags), BunResultMode(result_mode), namesPtr, namesCount));
}

extern "C" EncodedJSValue JSC__createStructure(JSC::JSGlobalObject* globalObject, JSC::JSCell* owner, uint32_t capacity, ExternColumnIdentifier* namesPtr)
extern "C" [[ZIG_EXPORT(nothrow)]] EncodedJSValue JSC__createStructure(JSC::JSGlobalObject* globalObject, JSC::JSCell* owner, Bun::ExternColumnIdentifier* namesPtr, size_t capacity)
{
auto& vm = JSC::getVM(globalObject);

Expand All @@ -441,7 +441,7 @@ extern "C" EncodedJSValue JSC__createStructure(JSC::JSGlobalObject* globalObject
for (uint32_t i = 0; i < capacity; i++) {
ExternColumnIdentifier& name = names[i];
if (name.isNamedColumn()) {
propertyNames.add(Identifier::fromString(vm, name.name.toWTFString()));
propertyNames.add(Identifier::fromString(vm, name.name.transferToWTFString()));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably not correct. This will make it so the property name is no longer usable and that string may outlive this call

}
nonDuplicateCount += !name.isDuplicateColumn();
if (nonDuplicateCount == JSFinalObject::maxInlineCapacity) {
Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4640,7 +4640,7 @@ bool GlobalObject::untrackFFIFunction(JSC::JSFunction* function)
});
}

extern "C" void Zig__GlobalObject__destructOnExit(Zig::GlobalObject* globalObject)
extern "C" [[ZIG_EXPORT(nothrow)]] void Zig__GlobalObject__destructOnExit(Zig::GlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);
if (vm.entryScope) {
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/shared-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export const sharedTypes: Record<string, string> = {
"JSC::CustomGetterSetter": "bun.jsc.CustomGetterSetter",
"JSC::SourceProvider": "bun.jsc.SourceProvider",
"JSC::CallFrame": "bun.jsc.CallFrame",
"JSC::JSCell": "bun.jsc.JSCell",
"Bun::ExternColumnIdentifier": "bun.jsc.JSObject.ExternColumnIdentifier",
"JSC::JSObject": "bun.jsc.JSObject",
"JSC::JSString": "bun.jsc.JSString",
};
Expand Down
8 changes: 4 additions & 4 deletions src/sql/mysql/protocol/DecodeBinaryValue.zig
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
}
var buffer: [22]u8 = undefined;
const slice = std.fmt.bufPrint(&buffer, "{d}", .{val}) catch unreachable;
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
}
const val = try reader.int(i64);
if (val >= std.math.minInt(i32) and val <= std.math.maxInt(i32)) {
Expand All @@ -72,7 +72,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
}
var buffer: [22]u8 = undefined;
const slice = std.fmt.bufPrint(&buffer, "{d}", .{val}) catch unreachable;
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_FLOAT => {
if (raw) {
Expand Down Expand Up @@ -135,7 +135,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
defer string_data.deinit();

const slice = string_data.slice();
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},

.MYSQL_TYPE_JSON => {
Expand All @@ -147,7 +147,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
var string_data = try reader.encodeLenString();
defer string_data.deinit();
const slice = string_data.slice();
return SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_BIT => {
// BIT(1) is a special case, it's a boolean
Expand Down
6 changes: 3 additions & 3 deletions src/sql/mysql/protocol/ResultSet.zig
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ pub const Row = struct {
}

const slice = value.slice();
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_JSON => {
const slice = value.slice();
cell.* = SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
cell.* = SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
Comment thread
nektro marked this conversation as resolved.

.MYSQL_TYPE_DATE, .MYSQL_TYPE_TIME, .MYSQL_TYPE_DATETIME, .MYSQL_TYPE_TIMESTAMP => {
Expand All @@ -135,7 +135,7 @@ pub const Row = struct {
},
else => {
const slice = value.slice();
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
};
}
Expand Down
28 changes: 14 additions & 14 deletions src/sql/postgres/DataCell.zig
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..];
defer if (needs_dynamic_buffer) bun.default_allocator.free(buffer);
const unescaped = unescapePostgresString(str_bytes, buffer) catch return error.InvalidByteSequence;
try array.append(bun.default_allocator, SQLDataCell{ .tag = .json, .value = .{ .json = if (unescaped.len > 0) String.cloneUTF8(unescaped).value.WTFStringImpl else null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .json, .value = .{ .json = if (unescaped.len > 0) String.cloneUTF8(unescaped) else .dead }, .free_value = 1 });
slice = trySlice(slice, current_idx + 1);
Comment on lines 155 to 159

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix reversed buffer allocation condition (can cause spurious BufferTooSmall and heap churn)

needs_dynamic_buffer should be true when input exceeds the stack buffer. Also, prefer slicing the stack buffer to str_bytes.len.

Apply:

-                        const needs_dynamic_buffer = str_bytes.len < stack_buffer.len;
-                        const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..];
+                        const needs_dynamic_buffer = str_bytes.len > stack_buffer.len;
+                        const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..str_bytes.len];

And similarly below:

-                const needs_dynamic_buffer = str_bytes.len < stack_buffer.len;
-                const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..];
+                const needs_dynamic_buffer = str_bytes.len > stack_buffer.len;
+                const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..str_bytes.len];

Also applies to: 171-176

continue;
},
Expand All @@ -164,15 +164,15 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
const str_bytes = slice[1..current_idx];
if (str_bytes.len == 0) {
// empty string
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = .dead }, .free_value = 1 });
slice = trySlice(slice, current_idx + 1);
continue;
}
const needs_dynamic_buffer = str_bytes.len < stack_buffer.len;
const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..];
defer if (needs_dynamic_buffer) bun.default_allocator.free(buffer);
const string_bytes = unescapePostgresString(str_bytes, buffer) catch return error.InvalidByteSequence;
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (string_bytes.len > 0) String.cloneUTF8(string_bytes).value.WTFStringImpl else null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (string_bytes.len > 0) String.cloneUTF8(string_bytes) else .dead }, .free_value = 1 });

slice = trySlice(slice, current_idx + 1);
continue;
Expand Down Expand Up @@ -243,9 +243,9 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
} else {
// the only escape sequency possible here is \b
if (bun.strings.eqlComptime(element, "\\b")) {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8("\x08").value.WTFStringImpl }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.static("\x08") }, .free_value = 1 });
} else {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element).value.WTFStringImpl else null }, .free_value = 0 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 0 });
}
Comment on lines +248 to 249

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Memory leak: free_value must be 1 for cloneUTF8 result

This path creates a BunString via cloneUTF8 but sets free_value=0, so deref is skipped and leaks.

Apply:

-                                try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 0 });
+                                try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 1 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 0 });
}
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 1 });
🤖 Prompt for AI Agents
In src/sql/postgres/DataCell.zig around lines 248-249, the code clones element
with bun.String.cloneUTF8 but sets free_value = 0 causing a memory leak; change
the free_value to 1 when cloneUTF8 is used (i.e., when element.len > 0) and keep
free_value = 0 for the .dead branch so the cloned BunString is properly freed.

}
slice = trySlice(slice, current_idx);
Expand Down Expand Up @@ -405,7 +405,7 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
if (bigint) {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .int8, .value = .{ .int8 = std.fmt.parseInt(i64, element, 0) catch return error.UnsupportedArrayFormat } });
} else {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element).value.WTFStringImpl else null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 1 });
}
slice = trySlice(slice, current_idx);
continue;
Expand Down Expand Up @@ -536,7 +536,7 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
// .int8 is a 64-bit integer always string
return SQLDataCell{ .tag = .int8, .value = .{ .int8 = std.fmt.parseInt(i64, bytes, 0) catch 0 } };
} else {
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
}
},
.float8 => {
Expand Down Expand Up @@ -566,14 +566,14 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,

// if is binary format lets display as a string because JS cant handle it in a safe way
const result = parseBinaryNumeric(bytes, &numeric_buffer) catch return error.UnsupportedNumericFormat;
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(result.slice()).value.WTFStringImpl }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(result.slice()) }, .free_value = 1 };
} else {
// nice text is actually what we want here
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
}
},
.jsonb, .json => {
return SQLDataCell{ .tag = .json, .value = .{ .json = if (bytes.len > 0) String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .json, .value = .{ .json = if (bytes.len > 0) String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
},
.bool => {
if (binary) {
Expand Down Expand Up @@ -614,7 +614,7 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
var buffer: [32]u8 = undefined;
const len = Postgres__formatTime(microseconds, &buffer, buffer.len);

return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]).value.WTFStringImpl }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]) }, .free_value = 1 };
} else if (tag == .timetz and bytes.len == 12) {
// PostgreSQL sends timetz as microseconds since midnight (8 bytes) + timezone offset in seconds (4 bytes)
const microseconds = @byteSwap(@as(i64, @bitCast(bytes[0..8].*)));
Expand All @@ -624,13 +624,13 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
var buffer: [48]u8 = undefined;
const len = Postgres__formatTimeTz(microseconds, tz_offset_seconds, &buffer, buffer.len);

return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]).value.WTFStringImpl }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]) }, .free_value = 1 };
} else {
return error.InvalidBinaryData;
}
} else {
// Text format - just return as string
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
}
},

Expand Down Expand Up @@ -697,7 +697,7 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
return try parseArray(bytes, bigint, tag, globalObject, null, false);
},
else => {
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
},
}
}
Expand Down
11 changes: 3 additions & 8 deletions src/sql/postgres/PostgresSQLConnection.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1725,11 +1725,10 @@ pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.enum_litera
.ErrorResponse => {
var err: protocol.ErrorResponse = undefined;
try err.decodeInternal(Context, reader);
errdefer err.deinit();

if (this.status == .connecting or this.status == .sent_startup_message) {
defer {
err.deinit();
}
defer err.deinit();
this.failWithJSValue(err.toJS(this.globalObject));

// it shouldn't enqueue any requests while connecting
Expand All @@ -1742,11 +1741,7 @@ pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.enum_litera
return error.ExpectedRequest;
};
var is_error_owned = true;
defer {
if (is_error_owned) {
err.deinit();
}
}
defer if (is_error_owned) err.deinit();
if (request.statement) |stmt| {
if (stmt.status == PostgresSQLStatement.Status.parsing) {
stmt.status = PostgresSQLStatement.Status.failed;
Expand Down
7 changes: 1 addition & 6 deletions src/sql/postgres/PostgresSQLStatement.zig
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,7 @@ pub fn structure(this: *PostgresSQLStatement, owner: JSValue, globalObject: *jsc
if (nonDuplicatedCount > 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,
), null);
this.cached_structure.set(globalObject, jsc.JSObject.createStructure(globalObject, owner, @truncate(ids.len), ids.ptr), null);
}

return this.cached_structure;
Expand Down
Loading
Loading