mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
Support jsonb, idle_timeout, connection_timeout, max_lifetime timeouts in bun:sql. Add onopen and onclose callbacks. Fix missing "code" property appearing in errors. Add error codes for postgres. (#16045)
This commit is contained in:
@@ -15,7 +15,7 @@ const int4 = postgres.int4;
|
||||
const int8 = postgres.int8;
|
||||
const PostgresInt64 = postgres.PostgresInt64;
|
||||
const types = postgres.types;
|
||||
|
||||
const AnyPostgresError = postgres.AnyPostgresError;
|
||||
pub const ArrayList = struct {
|
||||
array: *std.ArrayList(u8),
|
||||
|
||||
@@ -23,11 +23,11 @@ pub const ArrayList = struct {
|
||||
return this.array.items.len;
|
||||
}
|
||||
|
||||
pub fn write(this: @This(), bytes: []const u8) anyerror!void {
|
||||
pub fn write(this: @This(), bytes: []const u8) AnyPostgresError!void {
|
||||
try this.array.appendSlice(bytes);
|
||||
}
|
||||
|
||||
pub fn pwrite(this: @This(), bytes: []const u8, i: usize) anyerror!void {
|
||||
pub fn pwrite(this: @This(), bytes: []const u8, i: usize) AnyPostgresError!void {
|
||||
@memcpy(this.array.items[i..][0..bytes.len], bytes);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ pub const StackReader = struct {
|
||||
pub fn ensureCapacity(this: StackReader, count: usize) bool {
|
||||
return this.buffer.len >= (this.offset.* + count);
|
||||
}
|
||||
pub fn read(this: StackReader, count: usize) anyerror!Data {
|
||||
pub fn read(this: StackReader, count: usize) AnyPostgresError!Data {
|
||||
const offset = this.offset.*;
|
||||
if (!this.ensureCapacity(count)) {
|
||||
return error.ShortRead;
|
||||
@@ -82,7 +82,7 @@ pub const StackReader = struct {
|
||||
.temporary = this.buffer[offset..this.offset.*],
|
||||
};
|
||||
}
|
||||
pub fn readZ(this: StackReader) anyerror!Data {
|
||||
pub fn readZ(this: StackReader) AnyPostgresError!Data {
|
||||
const remaining = this.peek();
|
||||
if (bun.strings.indexOfChar(remaining, 0)) |zero| {
|
||||
this.skip(zero + 1);
|
||||
@@ -98,8 +98,8 @@ pub const StackReader = struct {
|
||||
pub fn NewWriterWrap(
|
||||
comptime Context: type,
|
||||
comptime offsetFn_: (fn (ctx: Context) usize),
|
||||
comptime writeFunction_: (fn (ctx: Context, bytes: []const u8) anyerror!void),
|
||||
comptime pwriteFunction_: (fn (ctx: Context, bytes: []const u8, offset: usize) anyerror!void),
|
||||
comptime writeFunction_: (fn (ctx: Context, bytes: []const u8) AnyPostgresError!void),
|
||||
comptime pwriteFunction_: (fn (ctx: Context, bytes: []const u8, offset: usize) AnyPostgresError!void),
|
||||
) type {
|
||||
return struct {
|
||||
wrapped: Context,
|
||||
@@ -111,7 +111,7 @@ pub fn NewWriterWrap(
|
||||
|
||||
pub const WrappedWriter = @This();
|
||||
|
||||
pub inline fn write(this: @This(), data: []const u8) anyerror!void {
|
||||
pub inline fn write(this: @This(), data: []const u8) AnyPostgresError!void {
|
||||
try writeFn(this.wrapped, data);
|
||||
}
|
||||
|
||||
@@ -119,16 +119,16 @@ pub fn NewWriterWrap(
|
||||
index: usize,
|
||||
context: WrappedWriter,
|
||||
|
||||
pub fn write(this: LengthWriter) anyerror!void {
|
||||
pub fn write(this: LengthWriter) AnyPostgresError!void {
|
||||
try this.context.pwrite(&Int32(this.context.offset() - this.index), this.index);
|
||||
}
|
||||
|
||||
pub fn writeExcludingSelf(this: LengthWriter) anyerror!void {
|
||||
pub fn writeExcludingSelf(this: LengthWriter) AnyPostgresError!void {
|
||||
try this.context.pwrite(&Int32(this.context.offset() -| (this.index + 4)), this.index);
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn length(this: @This()) anyerror!LengthWriter {
|
||||
pub inline fn length(this: @This()) AnyPostgresError!LengthWriter {
|
||||
const i = this.offset();
|
||||
try this.int4(0);
|
||||
return LengthWriter{
|
||||
@@ -141,7 +141,7 @@ pub fn NewWriterWrap(
|
||||
return offsetFn(this.wrapped);
|
||||
}
|
||||
|
||||
pub inline fn pwrite(this: @This(), data: []const u8, i: usize) anyerror!void {
|
||||
pub inline fn pwrite(this: @This(), data: []const u8, i: usize) AnyPostgresError!void {
|
||||
try pwriteFn(this.wrapped, data, i);
|
||||
}
|
||||
|
||||
@@ -208,81 +208,81 @@ pub fn NewWriterWrap(
|
||||
|
||||
pub const FieldType = enum(u8) {
|
||||
/// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a localized translation of one of these. Always present.
|
||||
S = 'S',
|
||||
severity = 'S',
|
||||
|
||||
/// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message). This is identical to the S field except that the contents are never localized. This is present only in messages generated by PostgreSQL versions 9.6 and later.
|
||||
V = 'V',
|
||||
localized_severity = 'V',
|
||||
|
||||
/// Code: the SQLSTATE code for the error (see Appendix A). Not localizable. Always present.
|
||||
C = 'C',
|
||||
code = 'C',
|
||||
|
||||
/// Message: the primary human-readable error message. This should be accurate but terse (typically one line). Always present.
|
||||
M = 'M',
|
||||
message = 'M',
|
||||
|
||||
/// Detail: an optional secondary error message carrying more detail about the problem. Might run to multiple lines.
|
||||
D = 'D',
|
||||
detail = 'D',
|
||||
|
||||
/// Hint: an optional suggestion what to do about the problem. This is intended to differ from Detail in that it offers advice (potentially inappropriate) rather than hard facts. Might run to multiple lines.
|
||||
H = 'H',
|
||||
hint = 'H',
|
||||
|
||||
/// Position: the field value is a decimal ASCII integer, indicating an error cursor position as an index into the original query string. The first character has index 1, and positions are measured in characters not bytes.
|
||||
P = 'P',
|
||||
position = 'P',
|
||||
|
||||
/// Internal position: this is defined the same as the P field, but it is used when the cursor position refers to an internally generated command rather than the one submitted by the client. The q field will always appear when this field appears.
|
||||
p = 'p',
|
||||
internal_position = 'p',
|
||||
|
||||
/// Internal query: the text of a failed internally-generated command. This could be, for example, an SQL query issued by a PL/pgSQL function.
|
||||
q = 'q',
|
||||
internal = 'q',
|
||||
|
||||
/// Where: an indication of the context in which the error occurred. Presently this includes a call stack traceback of active procedural language functions and internally-generated queries. The trace is one entry per line, most recent first.
|
||||
W = 'W',
|
||||
where = 'W',
|
||||
|
||||
/// Schema name: if the error was associated with a specific database object, the name of the schema containing that object, if any.
|
||||
s = 's',
|
||||
schema = 's',
|
||||
|
||||
/// Table name: if the error was associated with a specific table, the name of the table. (Refer to the schema name field for the name of the table's schema.)
|
||||
t = 't',
|
||||
table = 't',
|
||||
|
||||
/// Column name: if the error was associated with a specific table column, the name of the column. (Refer to the schema and table name fields to identify the table.)
|
||||
c = 'c',
|
||||
column = 'c',
|
||||
|
||||
/// Data type name: if the error was associated with a specific data type, the name of the data type. (Refer to the schema name field for the name of the data type's schema.)
|
||||
d = 'd',
|
||||
datatype = 'd',
|
||||
|
||||
/// Constraint name: if the error was associated with a specific constraint, the name of the constraint. Refer to fields listed above for the associated table or domain. (For this purpose, indexes are treated as constraints, even if they weren't created with constraint syntax.)
|
||||
n = 'n',
|
||||
constraint = 'n',
|
||||
|
||||
/// File: the file name of the source-code location where the error was reported.
|
||||
F = 'F',
|
||||
file = 'F',
|
||||
|
||||
/// Line: the line number of the source-code location where the error was reported.
|
||||
L = 'L',
|
||||
line = 'L',
|
||||
|
||||
/// Routine: the name of the source-code routine reporting the error.
|
||||
R = 'R',
|
||||
routine = 'R',
|
||||
|
||||
_,
|
||||
};
|
||||
|
||||
pub const FieldMessage = union(FieldType) {
|
||||
S: String,
|
||||
V: String,
|
||||
C: String,
|
||||
M: String,
|
||||
D: String,
|
||||
H: String,
|
||||
P: String,
|
||||
p: String,
|
||||
q: String,
|
||||
W: String,
|
||||
s: String,
|
||||
t: String,
|
||||
c: String,
|
||||
d: String,
|
||||
n: String,
|
||||
F: String,
|
||||
L: String,
|
||||
R: String,
|
||||
severity: String,
|
||||
localized_severity: String,
|
||||
code: String,
|
||||
message: String,
|
||||
detail: String,
|
||||
hint: String,
|
||||
position: String,
|
||||
internal_position: String,
|
||||
internal: String,
|
||||
where: String,
|
||||
schema: String,
|
||||
table: String,
|
||||
column: String,
|
||||
datatype: String,
|
||||
constraint: String,
|
||||
file: String,
|
||||
line: String,
|
||||
routine: String,
|
||||
|
||||
pub fn format(this: FieldMessage, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
switch (this) {
|
||||
@@ -319,24 +319,25 @@ pub const FieldMessage = union(FieldType) {
|
||||
|
||||
pub fn init(tag: FieldType, message: []const u8) !FieldMessage {
|
||||
return switch (tag) {
|
||||
.S => FieldMessage{ .S = String.createUTF8(message) },
|
||||
.V => FieldMessage{ .V = String.createUTF8(message) },
|
||||
.C => FieldMessage{ .C = String.createUTF8(message) },
|
||||
.M => FieldMessage{ .M = String.createUTF8(message) },
|
||||
.D => FieldMessage{ .D = String.createUTF8(message) },
|
||||
.H => FieldMessage{ .H = String.createUTF8(message) },
|
||||
.P => FieldMessage{ .P = String.createUTF8(message) },
|
||||
.p => FieldMessage{ .p = String.createUTF8(message) },
|
||||
.q => FieldMessage{ .q = String.createUTF8(message) },
|
||||
.W => FieldMessage{ .W = String.createUTF8(message) },
|
||||
.s => FieldMessage{ .s = String.createUTF8(message) },
|
||||
.t => FieldMessage{ .t = String.createUTF8(message) },
|
||||
.c => FieldMessage{ .c = String.createUTF8(message) },
|
||||
.d => FieldMessage{ .d = String.createUTF8(message) },
|
||||
.n => FieldMessage{ .n = String.createUTF8(message) },
|
||||
.F => FieldMessage{ .F = String.createUTF8(message) },
|
||||
.L => FieldMessage{ .L = String.createUTF8(message) },
|
||||
.R => FieldMessage{ .R = String.createUTF8(message) },
|
||||
.severity => FieldMessage{ .severity = String.createUTF8(message) },
|
||||
// Ignore this one for now.
|
||||
// .localized_severity => FieldMessage{ .localized_severity = String.createUTF8(message) },
|
||||
.code => FieldMessage{ .code = String.createUTF8(message) },
|
||||
.message => FieldMessage{ .message = String.createUTF8(message) },
|
||||
.detail => FieldMessage{ .detail = String.createUTF8(message) },
|
||||
.hint => FieldMessage{ .hint = String.createUTF8(message) },
|
||||
.position => FieldMessage{ .position = String.createUTF8(message) },
|
||||
.internal_position => FieldMessage{ .internal_position = String.createUTF8(message) },
|
||||
.internal => FieldMessage{ .internal = String.createUTF8(message) },
|
||||
.where => FieldMessage{ .where = String.createUTF8(message) },
|
||||
.schema => FieldMessage{ .schema = String.createUTF8(message) },
|
||||
.table => FieldMessage{ .table = String.createUTF8(message) },
|
||||
.column => FieldMessage{ .column = String.createUTF8(message) },
|
||||
.datatype => FieldMessage{ .datatype = String.createUTF8(message) },
|
||||
.constraint => FieldMessage{ .constraint = String.createUTF8(message) },
|
||||
.file => FieldMessage{ .file = String.createUTF8(message) },
|
||||
.line => FieldMessage{ .line = String.createUTF8(message) },
|
||||
.routine => FieldMessage{ .routine = String.createUTF8(message) },
|
||||
else => error.UnknownFieldType,
|
||||
};
|
||||
}
|
||||
@@ -348,8 +349,8 @@ pub fn NewReaderWrap(
|
||||
comptime peekFn_: (fn (ctx: Context) []const u8),
|
||||
comptime skipFn_: (fn (ctx: Context, count: usize) void),
|
||||
comptime ensureCapacityFn_: (fn (ctx: Context, count: usize) bool),
|
||||
comptime readFunction_: (fn (ctx: Context, count: usize) anyerror!Data),
|
||||
comptime readZ_: (fn (ctx: Context) anyerror!Data),
|
||||
comptime readFunction_: (fn (ctx: Context, count: usize) AnyPostgresError!Data),
|
||||
comptime readZ_: (fn (ctx: Context) AnyPostgresError!Data),
|
||||
) type {
|
||||
return struct {
|
||||
wrapped: Context,
|
||||
@@ -366,11 +367,11 @@ pub fn NewReaderWrap(
|
||||
markMessageStartFn(this.wrapped);
|
||||
}
|
||||
|
||||
pub inline fn read(this: @This(), count: usize) anyerror!Data {
|
||||
pub inline fn read(this: @This(), count: usize) AnyPostgresError!Data {
|
||||
return try readFn(this.wrapped, count);
|
||||
}
|
||||
|
||||
pub inline fn eatMessage(this: @This(), comptime msg_: anytype) anyerror!void {
|
||||
pub inline fn eatMessage(this: @This(), comptime msg_: anytype) AnyPostgresError!void {
|
||||
const msg = msg_[1..];
|
||||
try this.ensureCapacity(msg.len);
|
||||
|
||||
@@ -380,7 +381,7 @@ pub fn NewReaderWrap(
|
||||
return error.InvalidMessage;
|
||||
}
|
||||
|
||||
pub fn skip(this: @This(), count: usize) anyerror!void {
|
||||
pub fn skip(this: @This(), count: usize) AnyPostgresError!void {
|
||||
skipFn(this.wrapped, count);
|
||||
}
|
||||
|
||||
@@ -388,11 +389,11 @@ pub fn NewReaderWrap(
|
||||
return peekFn(this.wrapped);
|
||||
}
|
||||
|
||||
pub inline fn readZ(this: @This()) anyerror!Data {
|
||||
pub inline fn readZ(this: @This()) AnyPostgresError!Data {
|
||||
return try readZFn(this.wrapped);
|
||||
}
|
||||
|
||||
pub inline fn ensureCapacity(this: @This(), count: usize) anyerror!void {
|
||||
pub inline fn ensureCapacity(this: @This(), count: usize) AnyPostgresError!void {
|
||||
if (!ensureCapacityFn(this.wrapped, count)) {
|
||||
return error.ShortRead;
|
||||
}
|
||||
@@ -457,7 +458,7 @@ pub fn NewWriter(comptime Context: type) type {
|
||||
|
||||
fn decoderWrap(comptime Container: type, comptime decodeFn: anytype) type {
|
||||
return struct {
|
||||
pub fn decode(this: *Container, context: anytype) anyerror!void {
|
||||
pub fn decode(this: *Container, context: anytype) AnyPostgresError!void {
|
||||
const Context = @TypeOf(context);
|
||||
try decodeFn(this, Context, NewReader(Context){ .wrapped = context });
|
||||
}
|
||||
@@ -466,7 +467,7 @@ fn decoderWrap(comptime Container: type, comptime decodeFn: anytype) type {
|
||||
|
||||
fn writeWrap(comptime Container: type, comptime writeFn: anytype) type {
|
||||
return struct {
|
||||
pub fn write(this: *Container, context: anytype) anyerror!void {
|
||||
pub fn write(this: *Container, context: anytype) AnyPostgresError!void {
|
||||
const Context = @TypeOf(context);
|
||||
try writeFn(this, Context, NewWriter(Context){ .wrapped = context });
|
||||
}
|
||||
@@ -538,9 +539,6 @@ pub const Authentication = union(enum) {
|
||||
},
|
||||
5 => {
|
||||
if (message_length != 12) return error.InvalidMessageLength;
|
||||
if (!try reader.expectInt(u32, 5)) {
|
||||
return error.InvalidMessage;
|
||||
}
|
||||
var salt_data = try reader.bytes(4);
|
||||
defer salt_data.deinit();
|
||||
this.* = .{
|
||||
@@ -722,23 +720,117 @@ pub const ErrorResponse = struct {
|
||||
var b = bun.StringBuilder{};
|
||||
defer b.deinit(bun.default_allocator);
|
||||
|
||||
for (this.messages.items) |msg| {
|
||||
b.cap += switch (msg) {
|
||||
// Pre-calculate capacity to avoid reallocations
|
||||
for (this.messages.items) |*msg| {
|
||||
b.cap += switch (msg.*) {
|
||||
inline else => |m| m.utf8ByteLength(),
|
||||
} + 1;
|
||||
}
|
||||
b.allocate(bun.default_allocator) catch {};
|
||||
|
||||
for (this.messages.items) |msg| {
|
||||
var str = switch (msg) {
|
||||
inline else => |m| m.toUTF8(bun.default_allocator),
|
||||
};
|
||||
defer str.deinit();
|
||||
_ = b.append(str.slice());
|
||||
_ = b.append("\n");
|
||||
// Build a more structured error message
|
||||
var severity: String = String.dead;
|
||||
var code: String = String.dead;
|
||||
var message: String = String.dead;
|
||||
var detail: String = String.dead;
|
||||
var hint: String = String.dead;
|
||||
var position: String = String.dead;
|
||||
var where: String = String.dead;
|
||||
var schema: String = String.dead;
|
||||
var table: String = String.dead;
|
||||
var column: String = String.dead;
|
||||
var datatype: String = String.dead;
|
||||
var constraint: String = String.dead;
|
||||
var file: String = String.dead;
|
||||
var line: String = String.dead;
|
||||
var routine: String = String.dead;
|
||||
|
||||
for (this.messages.items) |*msg| {
|
||||
switch (msg.*) {
|
||||
.severity => |str| severity = str,
|
||||
.code => |str| code = str,
|
||||
.message => |str| message = str,
|
||||
.detail => |str| detail = str,
|
||||
.hint => |str| hint = str,
|
||||
.position => |str| position = str,
|
||||
.where => |str| where = str,
|
||||
.schema => |str| schema = str,
|
||||
.table => |str| table = str,
|
||||
.column => |str| column = str,
|
||||
.datatype => |str| datatype = str,
|
||||
.constraint => |str| constraint = str,
|
||||
.file => |str| file = str,
|
||||
.line => |str| line = str,
|
||||
.routine => |str| routine = str,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
return globalObject.createSyntaxErrorInstance("Postgres error occurred\n{s}", .{b.allocatedSlice()[0..b.len]});
|
||||
var needs_newline = false;
|
||||
construct_message: {
|
||||
if (!message.isEmpty()) {
|
||||
_ = b.appendStr(message);
|
||||
needs_newline = true;
|
||||
break :construct_message;
|
||||
}
|
||||
if (!detail.isEmpty()) {
|
||||
if (needs_newline) {
|
||||
_ = b.append("\n");
|
||||
} else {
|
||||
_ = b.append(" ");
|
||||
}
|
||||
needs_newline = true;
|
||||
_ = b.appendStr(detail);
|
||||
}
|
||||
if (!hint.isEmpty()) {
|
||||
if (needs_newline) {
|
||||
_ = b.append("\n");
|
||||
} else {
|
||||
_ = b.append(" ");
|
||||
}
|
||||
needs_newline = true;
|
||||
_ = b.appendStr(hint);
|
||||
}
|
||||
}
|
||||
|
||||
const possible_fields = .{
|
||||
.{ "detail", detail, void },
|
||||
.{ "hint", hint, void },
|
||||
.{ "column", column, void },
|
||||
.{ "constraint", constraint, void },
|
||||
.{ "datatype", datatype, void },
|
||||
.{ "errno", code, i32 },
|
||||
.{ "position", position, i32 },
|
||||
.{ "schema", schema, void },
|
||||
.{ "table", table, void },
|
||||
.{ "where", where, void },
|
||||
};
|
||||
|
||||
const error_code: JSC.Error =
|
||||
// https://www.postgresql.org/docs/8.1/errcodes-appendix.html
|
||||
if (code.toInt32() orelse 0 == 42601)
|
||||
JSC.Error.ERR_POSTGRES_SYNTAX_ERROR
|
||||
else
|
||||
JSC.Error.ERR_POSTGRES_SERVER_ERROR;
|
||||
const err = error_code.fmt(globalObject, "{s}", .{b.allocatedSlice()[0..b.len]});
|
||||
|
||||
inline for (possible_fields) |field| {
|
||||
if (!field.@"1".isEmpty()) {
|
||||
const value = brk: {
|
||||
if (field.@"2" == i32) {
|
||||
if (field.@"1".toInt32()) |val| {
|
||||
break :brk JSC.JSValue.jsNumberFromInt32(val);
|
||||
}
|
||||
}
|
||||
|
||||
break :brk field.@"1".toJS(globalObject);
|
||||
};
|
||||
|
||||
err.put(globalObject, JSC.ZigString.static(field.@"0"), value);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -847,7 +939,7 @@ pub const FormatCode = enum {
|
||||
pub const null_int4 = 4294967295;
|
||||
|
||||
pub const DataRow = struct {
|
||||
pub fn decode(context: anytype, comptime ContextType: type, reader: NewReader(ContextType), comptime forEach: fn (@TypeOf(context), index: u32, bytes: ?*Data) anyerror!bool) anyerror!void {
|
||||
pub fn decode(context: anytype, comptime ContextType: type, reader: NewReader(ContextType), comptime forEach: fn (@TypeOf(context), index: u32, bytes: ?*Data) AnyPostgresError!bool) AnyPostgresError!void {
|
||||
var remaining_bytes = try reader.length();
|
||||
remaining_bytes -|= 4;
|
||||
|
||||
@@ -885,7 +977,7 @@ pub const FieldDescription = struct {
|
||||
this.name.deinit();
|
||||
}
|
||||
|
||||
pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void {
|
||||
pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) AnyPostgresError!void {
|
||||
var name = try reader.readZ();
|
||||
errdefer {
|
||||
name.deinit();
|
||||
@@ -1355,6 +1447,29 @@ pub const NoticeResponse = struct {
|
||||
}
|
||||
}
|
||||
pub const decode = decoderWrap(NoticeResponse, decodeInternal).decode;
|
||||
|
||||
pub fn toJS(this: NoticeResponse, globalObject: *JSC.JSGlobalObject) JSValue {
|
||||
var b = bun.StringBuilder{};
|
||||
defer b.deinit(bun.default_allocator);
|
||||
|
||||
for (this.messages.items) |msg| {
|
||||
b.cap += switch (msg) {
|
||||
inline else => |m| m.utf8ByteLength(),
|
||||
} + 1;
|
||||
}
|
||||
b.allocate(bun.default_allocator) catch {};
|
||||
|
||||
for (this.messages.items) |msg| {
|
||||
var str = switch (msg) {
|
||||
inline else => |m| m.toUTF8(bun.default_allocator),
|
||||
};
|
||||
defer str.deinit();
|
||||
_ = b.append(str.slice());
|
||||
_ = b.append("\n");
|
||||
}
|
||||
|
||||
return JSC.ZigString.init(b.allocatedSlice()[0..b.len]).toJS(globalObject);
|
||||
}
|
||||
};
|
||||
|
||||
pub const CopyFail = struct {
|
||||
|
||||
@@ -12,6 +12,7 @@ const JSValue = JSC.JSValue;
|
||||
const JSC = bun.JSC;
|
||||
const short = postgres.short;
|
||||
const int4 = postgres.int4;
|
||||
const AnyPostgresError = postgres.AnyPostgresError;
|
||||
|
||||
// select b.typname, b.oid, b.typarray
|
||||
// from pg_catalog.pg_type a
|
||||
@@ -169,8 +170,17 @@ pub const Tag = enum(short) {
|
||||
bit_array = 1561,
|
||||
varbit_array = 1563,
|
||||
numeric_array = 1231,
|
||||
jsonb = 3802,
|
||||
jsonb_array = 3807,
|
||||
// Not really sure what this is.
|
||||
jsonpath = 4072,
|
||||
jsonpath_array = 4073,
|
||||
_,
|
||||
|
||||
pub fn name(this: Tag) ?[]const u8 {
|
||||
return std.enums.tagName(Tag, this);
|
||||
}
|
||||
|
||||
pub fn isBinaryFormatSupported(this: Tag) bool {
|
||||
return switch (this) {
|
||||
// TODO: .int2_array, .float8_array,
|
||||
@@ -282,7 +292,7 @@ pub const Tag = enum(short) {
|
||||
globalObject: *JSC.JSGlobalObject,
|
||||
comptime Type: type,
|
||||
value: Type,
|
||||
) anyerror!JSValue {
|
||||
) AnyPostgresError!JSValue {
|
||||
switch (tag) {
|
||||
.numeric => {
|
||||
return numeric.toJS(globalObject, value);
|
||||
@@ -292,7 +302,7 @@ pub const Tag = enum(short) {
|
||||
return numeric.toJS(globalObject, value);
|
||||
},
|
||||
|
||||
.json => {
|
||||
.json, .jsonb => {
|
||||
return json.toJS(globalObject, value);
|
||||
},
|
||||
|
||||
@@ -326,7 +336,7 @@ pub const Tag = enum(short) {
|
||||
tag: Tag,
|
||||
globalObject: *JSC.JSGlobalObject,
|
||||
value: anytype,
|
||||
) anyerror!JSValue {
|
||||
) AnyPostgresError!JSValue {
|
||||
return toJSWithType(tag, globalObject, @TypeOf(value), value);
|
||||
}
|
||||
|
||||
@@ -363,16 +373,16 @@ pub const Tag = enum(short) {
|
||||
|
||||
// Ban these types:
|
||||
if (tag == .NumberObject) {
|
||||
return error.JSError;
|
||||
return globalObject.ERR_INVALID_ARG_TYPE("Number object is ambiguous and cannot be used as a PostgreSQL type", .{}).throw();
|
||||
}
|
||||
|
||||
if (tag == .BooleanObject) {
|
||||
return error.JSError;
|
||||
return globalObject.ERR_INVALID_ARG_TYPE("Boolean object is ambiguous and cannot be used as a PostgreSQL type", .{}).throw();
|
||||
}
|
||||
|
||||
// It's something internal
|
||||
if (!tag.isIndexable()) {
|
||||
return error.JSError;
|
||||
return globalObject.ERR_INVALID_ARG_TYPE("Unknown object is not a valid PostgreSQL type", .{}).throw();
|
||||
}
|
||||
|
||||
// We will JSON.stringify anything else.
|
||||
@@ -414,7 +424,7 @@ pub const string = struct {
|
||||
globalThis: *JSC.JSGlobalObject,
|
||||
comptime Type: type,
|
||||
value: Type,
|
||||
) anyerror!JSValue {
|
||||
) AnyPostgresError!JSValue {
|
||||
switch (comptime Type) {
|
||||
[:0]u8, []u8, []const u8, [:0]const u8 => {
|
||||
var str = String.fromUTF8(value);
|
||||
@@ -456,7 +466,7 @@ pub const numeric = struct {
|
||||
pub fn toJS(
|
||||
_: *JSC.JSGlobalObject,
|
||||
value: anytype,
|
||||
) anyerror!JSValue {
|
||||
) AnyPostgresError!JSValue {
|
||||
return JSValue.jsNumber(value);
|
||||
}
|
||||
};
|
||||
@@ -468,12 +478,12 @@ pub const json = struct {
|
||||
pub fn toJS(
|
||||
globalObject: *JSC.JSGlobalObject,
|
||||
value: *Data,
|
||||
) anyerror!JSValue {
|
||||
) AnyPostgresError!JSValue {
|
||||
defer value.deinit();
|
||||
var str = bun.String.fromUTF8(value.slice());
|
||||
defer str.deref();
|
||||
const parse_result = JSValue.parse(str.toJS(globalObject), globalObject);
|
||||
if (parse_result.isAnyError()) {
|
||||
if (parse_result.AnyPostgresError()) {
|
||||
return globalObject.throwValue(parse_result);
|
||||
}
|
||||
|
||||
@@ -488,7 +498,7 @@ pub const @"bool" = struct {
|
||||
pub fn toJS(
|
||||
_: *JSC.JSGlobalObject,
|
||||
value: bool,
|
||||
) anyerror!JSValue {
|
||||
) AnyPostgresError!JSValue {
|
||||
return JSValue.jsBoolean(value);
|
||||
}
|
||||
};
|
||||
@@ -548,7 +558,7 @@ pub const bytea = struct {
|
||||
pub fn toJS(
|
||||
globalObject: *JSC.JSGlobalObject,
|
||||
value: *Data,
|
||||
) anyerror!JSValue {
|
||||
) AnyPostgresError!JSValue {
|
||||
defer value.deinit();
|
||||
|
||||
// var slice = value.slice()[@min(1, value.len)..];
|
||||
|
||||
Reference in New Issue
Block a user