SQLite in Bun.sql (#21640)

### What does this PR do?

Support sqlite in the Bun.sql API

Fixes #18951
Fixes #19701

### How did you verify your code works?

tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
This commit is contained in:
Alistair Smith
2025-08-19 23:15:53 -07:00
committed by GitHub
parent 9b363e4ef6
commit 784271f85e
41 changed files with 11262 additions and 2740 deletions

View File

@@ -33,37 +33,103 @@ pub const AnyPostgresError = error{
UnknownFormatCode,
};
/// Options for creating a PostgresError
pub const PostgresErrorOptions = struct {
code: []const u8,
errno: ?[]const u8 = null,
detail: ?[]const u8 = null,
hint: ?[]const u8 = null,
severity: ?[]const u8 = null,
position: ?[]const u8 = null,
internalPosition: ?[]const u8 = null,
internalQuery: ?[]const u8 = null,
where: ?[]const u8 = null,
schema: ?[]const u8 = null,
table: ?[]const u8 = null,
column: ?[]const u8 = null,
dataType: ?[]const u8 = null,
constraint: ?[]const u8 = null,
file: ?[]const u8 = null,
line: ?[]const u8 = null,
routine: ?[]const u8 = null,
};
pub fn createPostgresError(
globalObject: *jsc.JSGlobalObject,
message: []const u8,
options: PostgresErrorOptions,
) bun.JSError!JSValue {
const bun_ns = (try globalObject.toJSValue().get(globalObject, "Bun")).?;
const sql_constructor = (try bun_ns.get(globalObject, "SQL")).?;
const pg_error_constructor = (try sql_constructor.get(globalObject, "PostgresError")).?;
const opts_obj = JSValue.createEmptyObject(globalObject, 0);
opts_obj.put(globalObject, jsc.ZigString.static("code"), jsc.ZigString.init(options.code).toJS(globalObject));
if (options.errno) |errno| opts_obj.put(globalObject, jsc.ZigString.static("errno"), jsc.ZigString.init(errno).toJS(globalObject));
if (options.detail) |detail| opts_obj.put(globalObject, jsc.ZigString.static("detail"), jsc.ZigString.init(detail).toJS(globalObject));
if (options.hint) |hint| opts_obj.put(globalObject, jsc.ZigString.static("hint"), jsc.ZigString.init(hint).toJS(globalObject));
if (options.severity) |severity| opts_obj.put(globalObject, jsc.ZigString.static("severity"), jsc.ZigString.init(severity).toJS(globalObject));
if (options.position) |pos| opts_obj.put(globalObject, jsc.ZigString.static("position"), jsc.ZigString.init(pos).toJS(globalObject));
if (options.internalPosition) |pos| opts_obj.put(globalObject, jsc.ZigString.static("internalPosition"), jsc.ZigString.init(pos).toJS(globalObject));
if (options.internalQuery) |query| opts_obj.put(globalObject, jsc.ZigString.static("internalQuery"), jsc.ZigString.init(query).toJS(globalObject));
if (options.where) |w| opts_obj.put(globalObject, jsc.ZigString.static("where"), jsc.ZigString.init(w).toJS(globalObject));
if (options.schema) |s| opts_obj.put(globalObject, jsc.ZigString.static("schema"), jsc.ZigString.init(s).toJS(globalObject));
if (options.table) |t| opts_obj.put(globalObject, jsc.ZigString.static("table"), jsc.ZigString.init(t).toJS(globalObject));
if (options.column) |c| opts_obj.put(globalObject, jsc.ZigString.static("column"), jsc.ZigString.init(c).toJS(globalObject));
if (options.dataType) |dt| opts_obj.put(globalObject, jsc.ZigString.static("dataType"), jsc.ZigString.init(dt).toJS(globalObject));
if (options.constraint) |c| opts_obj.put(globalObject, jsc.ZigString.static("constraint"), jsc.ZigString.init(c).toJS(globalObject));
if (options.file) |f| opts_obj.put(globalObject, jsc.ZigString.static("file"), jsc.ZigString.init(f).toJS(globalObject));
if (options.line) |l| opts_obj.put(globalObject, jsc.ZigString.static("line"), jsc.ZigString.init(l).toJS(globalObject));
if (options.routine) |r| opts_obj.put(globalObject, jsc.ZigString.static("routine"), jsc.ZigString.init(r).toJS(globalObject));
const args = [_]JSValue{
jsc.ZigString.init(message).toJS(globalObject),
opts_obj,
};
const JSC = @import("../../bun.js/javascript_core_c_api.zig");
var exception: JSC.JSValueRef = null;
const result = JSC.JSObjectCallAsConstructor(globalObject, pg_error_constructor.asObjectRef(), args.len, @ptrCast(&args), &exception);
if (exception != null) {
return bun.JSError.JSError;
}
return JSValue.fromRef(result);
}
pub fn postgresErrorToJS(globalObject: *jsc.JSGlobalObject, message: ?[]const u8, err: AnyPostgresError) JSValue {
const error_code: jsc.Error = switch (err) {
error.ConnectionClosed => .POSTGRES_CONNECTION_CLOSED,
error.ExpectedRequest => .POSTGRES_EXPECTED_REQUEST,
error.ExpectedStatement => .POSTGRES_EXPECTED_STATEMENT,
error.InvalidBackendKeyData => .POSTGRES_INVALID_BACKEND_KEY_DATA,
error.InvalidBinaryData => .POSTGRES_INVALID_BINARY_DATA,
error.InvalidByteSequence => .POSTGRES_INVALID_BYTE_SEQUENCE,
error.InvalidByteSequenceForEncoding => .POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODING,
error.InvalidCharacter => .POSTGRES_INVALID_CHARACTER,
error.InvalidMessage => .POSTGRES_INVALID_MESSAGE,
error.InvalidMessageLength => .POSTGRES_INVALID_MESSAGE_LENGTH,
error.InvalidQueryBinding => .POSTGRES_INVALID_QUERY_BINDING,
error.InvalidServerKey => .POSTGRES_INVALID_SERVER_KEY,
error.InvalidServerSignature => .POSTGRES_INVALID_SERVER_SIGNATURE,
error.MultidimensionalArrayNotSupportedYet => .POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET,
error.NullsInArrayNotSupportedYet => .POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET,
error.Overflow => .POSTGRES_OVERFLOW,
error.PBKDFD2 => .POSTGRES_AUTHENTICATION_FAILED_PBKDF2,
error.SASL_SIGNATURE_MISMATCH => .POSTGRES_SASL_SIGNATURE_MISMATCH,
error.SASL_SIGNATURE_INVALID_BASE64 => .POSTGRES_SASL_SIGNATURE_INVALID_BASE64,
error.TLSNotAvailable => .POSTGRES_TLS_NOT_AVAILABLE,
error.TLSUpgradeFailed => .POSTGRES_TLS_UPGRADE_FAILED,
error.UnexpectedMessage => .POSTGRES_UNEXPECTED_MESSAGE,
error.UNKNOWN_AUTHENTICATION_METHOD => .POSTGRES_UNKNOWN_AUTHENTICATION_METHOD,
error.UNSUPPORTED_AUTHENTICATION_METHOD => .POSTGRES_UNSUPPORTED_AUTHENTICATION_METHOD,
error.UnsupportedByteaFormat => .POSTGRES_UNSUPPORTED_BYTEA_FORMAT,
error.UnsupportedArrayFormat => .POSTGRES_UNSUPPORTED_ARRAY_FORMAT,
error.UnsupportedIntegerSize => .POSTGRES_UNSUPPORTED_INTEGER_SIZE,
error.UnsupportedNumericFormat => .POSTGRES_UNSUPPORTED_NUMERIC_FORMAT,
error.UnknownFormatCode => .POSTGRES_UNKNOWN_FORMAT_CODE,
const code = switch (err) {
error.ConnectionClosed => "ERR_POSTGRES_CONNECTION_CLOSED",
error.ExpectedRequest => "ERR_POSTGRES_EXPECTED_REQUEST",
error.ExpectedStatement => "ERR_POSTGRES_EXPECTED_STATEMENT",
error.InvalidBackendKeyData => "ERR_POSTGRES_INVALID_BACKEND_KEY_DATA",
error.InvalidBinaryData => "ERR_POSTGRES_INVALID_BINARY_DATA",
error.InvalidByteSequence => "ERR_POSTGRES_INVALID_BYTE_SEQUENCE",
error.InvalidByteSequenceForEncoding => "ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODING",
error.InvalidCharacter => "ERR_POSTGRES_INVALID_CHARACTER",
error.InvalidMessage => "ERR_POSTGRES_INVALID_MESSAGE",
error.InvalidMessageLength => "ERR_POSTGRES_INVALID_MESSAGE_LENGTH",
error.InvalidQueryBinding => "ERR_POSTGRES_INVALID_QUERY_BINDING",
error.InvalidServerKey => "ERR_POSTGRES_INVALID_SERVER_KEY",
error.InvalidServerSignature => "ERR_POSTGRES_INVALID_SERVER_SIGNATURE",
error.MultidimensionalArrayNotSupportedYet => "ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET",
error.NullsInArrayNotSupportedYet => "ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET",
error.Overflow => "ERR_POSTGRES_OVERFLOW",
error.PBKDFD2 => "ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2",
error.SASL_SIGNATURE_MISMATCH => "ERR_POSTGRES_SASL_SIGNATURE_MISMATCH",
error.SASL_SIGNATURE_INVALID_BASE64 => "ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64",
error.TLSNotAvailable => "ERR_POSTGRES_TLS_NOT_AVAILABLE",
error.TLSUpgradeFailed => "ERR_POSTGRES_TLS_UPGRADE_FAILED",
error.UnexpectedMessage => "ERR_POSTGRES_UNEXPECTED_MESSAGE",
error.UNKNOWN_AUTHENTICATION_METHOD => "ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHOD",
error.UNSUPPORTED_AUTHENTICATION_METHOD => "ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHOD",
error.UnsupportedByteaFormat => "ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMAT",
error.UnsupportedArrayFormat => "ERR_POSTGRES_UNSUPPORTED_ARRAY_FORMAT",
error.UnsupportedIntegerSize => "ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE",
error.UnsupportedNumericFormat => "ERR_POSTGRES_UNSUPPORTED_NUMERIC_FORMAT",
error.UnknownFormatCode => "ERR_POSTGRES_UNKNOWN_FORMAT_CODE",
error.JSError => {
return globalObject.takeException(error.JSError);
},
@@ -75,13 +141,17 @@ pub fn postgresErrorToJS(globalObject: *jsc.JSGlobalObject, message: ?[]const u8
bun.unreachablePanic("Assertion failed: ShortRead should be handled by the caller in postgres", .{});
},
};
if (message) |msg| {
return error_code.fmt(globalObject, "{s}", .{msg});
const msg = message orelse std.fmt.allocPrint(bun.default_allocator, "Failed to bind query: {s}", .{@errorName(err)}) catch unreachable;
defer {
if (message == null) bun.default_allocator.free(msg);
}
return error_code.fmt(globalObject, "Failed to bind query: {s}", .{@errorName(err)});
return createPostgresError(globalObject, msg, .{ .code = code }) catch |e| globalObject.takeError(e);
}
const bun = @import("bun");
const std = @import("std");
const jsc = bun.jsc;
const JSValue = jsc.JSValue;