aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/api')
-rw-r--r--src/bun.js/api/bun.zig1
-rw-r--r--src/bun.js/api/postgres.classes.ts40
-rw-r--r--src/bun.js/api/postgres.zig425
-rw-r--r--src/bun.js/api/postgres_messages.zig410
4 files changed, 876 insertions, 0 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 6b19f138d..8bc3ee962 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -4004,4 +4004,5 @@ pub const JSZlib = struct {
}
};
+pub usingnamespace @import("./postgres.zig");
pub usingnamespace @import("./bun/subprocess.zig");
diff --git a/src/bun.js/api/postgres.classes.ts b/src/bun.js/api/postgres.classes.ts
new file mode 100644
index 000000000..309c9a0db
--- /dev/null
+++ b/src/bun.js/api/postgres.classes.ts
@@ -0,0 +1,40 @@
+import { define } from "../scripts/class-definitions" assert { type: "bun:pg" };
+
+export default [
+ define({
+ name: "PostgresSQLDatabase",
+ construct: false,
+ finalize: true,
+ hasPendingActivity: true,
+ noConstructor: true,
+ klass: {},
+ proto: {
+ close: {
+ fn: "close",
+ length: 0,
+ },
+ query: {
+ fn: "query",
+ length: 4,
+ },
+ // prepare: {
+ // fn: "prepare",
+ // length: 2,
+ // },
+ // run: {
+ // fn: "run",
+ // length: 2,
+ // },
+ ref: {
+ fn: "ref",
+ length: 0,
+ },
+ unref: {
+ fn: "unref",
+ length: 0,
+ },
+ },
+ values: ["onClose", "onNotice", "onOpen", "onTimeout", "onDrain"],
+ JSType: "0b11101110",
+ }),
+];
diff --git a/src/bun.js/api/postgres.zig b/src/bun.js/api/postgres.zig
new file mode 100644
index 000000000..bd45ba7cb
--- /dev/null
+++ b/src/bun.js/api/postgres.zig
@@ -0,0 +1,425 @@
+const Bun = @This();
+const default_allocator = @import("bun").default_allocator;
+const bun = @import("bun");
+const Environment = bun.Environment;
+const NetworkThread = @import("bun").HTTP.NetworkThread;
+const Global = bun.Global;
+const strings = bun.strings;
+const string = bun.string;
+const Output = @import("bun").Output;
+const MutableString = @import("bun").MutableString;
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const JSC = @import("bun").JSC;
+const JSValue = JSC.JSValue;
+const JSGlobalObject = JSC.JSGlobalObject;
+
+const uws = bun.uws;
+const Socket = uws.NewSocketHandler(false);
+const SocketContext = uws.SocketContext;
+const Messages = @import("./postgres_messages.zig");
+
+const ErrorCode = enum(i32) {
+ cancel,
+ invalid_response,
+ timeout,
+ closed,
+ failed_to_write,
+ failed_to_connect,
+ failed_to_allocate_memory,
+ invalid_utf8,
+ ended,
+ unknown,
+
+ pub const status = bun.enumMap(ErrorCode, .{
+ .{ .cancel, "cancel" },
+ .{ .invalid_response, "invalidResponse" },
+ .{ .timeout, "timeout" },
+ .{ .closed, "closed" },
+ .{ .failed_to_write, "failedToWrite" },
+ .{ .failed_to_connect, "failedToConnect" },
+ .{ .failed_to_allocate_memory, "failedToAllocateMemory" },
+ .{ .invalid_utf8, "invalidUtf8" },
+ .{ .ended, "ended" },
+ .{ .unknown, "unknown" },
+ });
+
+ pub const code = bun.enumMap(ErrorCode, .{
+ .{ .cancel, "POSTGRES_ERROR_CANCEL" },
+ .{ .invalid_response, "POSTGRES_ERROR_INVALID_RESPONSE" },
+ .{ .timeout, "POSTGRES_ERROR_TIMEOUT" },
+ .{ .closed, "POSTGRES_ERROR_CLOSED" },
+ .{ .failed_to_write, "POSTGRES_ERROR_FAILED_TO_WRITE" },
+ .{ .failed_to_connect, "POSTGRES_ERROR_FAILED_TO_CONNECT" },
+ .{ .failed_to_allocate_memory, "POSTGRES_ERROR_FAILED_TO_ALLOCATE_MEMORY" },
+ .{ .invalid_utf8, "POSTGRES_ERROR_INVALID_UTF8" },
+ .{ .ended, "POSTGRES_ERROR_ENDED" },
+ .{ .unknown, "POSTGRES_ERROR_UNKNOWN" },
+ });
+
+ pub const label = bun.enumMap(ErrorCode, .{
+ .{ .cancel, "The connection was cancelled" },
+ .{ .invalid_response, "The connection has an invalid response" },
+ .{ .timeout, "The connection timed out" },
+ .{ .closed, "The connection was closed" },
+ .{ .failed_to_write, "The connection failed to write" },
+ .{ .failed_to_connect, "The connection failed to connect" },
+ .{ .failed_to_allocate_memory, "Failed to allocate memory" },
+ .{ .invalid_utf8, "Received invalid UTF-8" },
+ .{ .ended, "The connection was ended" },
+ .{ .unknown, "An unknown error occurred" },
+ });
+
+ pub fn toErrorInstance(
+ this: ErrorCode,
+ globalObject: *JSC.JSGlobalObject,
+ ) JSC.JSValue {
+ var instance = globalObject.createErrorInstance(
+ "{s}",
+ .{this.label()},
+ );
+ instance.put("code", JSC.ZigString.init(this.code()).toValueGC(globalObject));
+ instance.put("name", JSC.ZigString.static("PostgresError").toValueGC(globalObject));
+ return instance;
+ }
+};
+
+const ConnectionOptions = union(enum) {
+ pub const TCP = struct {
+ hostname: []const u8 = "localhost",
+ port: u16 = 5432,
+ database: []const u8 = "postgres",
+ user: []const u8 = "",
+ password: []const u8 = "",
+ };
+ tcp: TCP,
+ tls: struct {
+ tcp: TCP,
+ },
+};
+
+pub const PostgresData = struct {
+ tcp_ctx: ?*uws.SocketContext = null,
+};
+
+pub const Protocol = struct {};
+
+pub const PostgresConnection = struct {
+ const log = Output.scoped(.PostgresConnection, false);
+
+ tcp: Socket,
+ poll_ref: JSC.PollRef = .{},
+
+ pub fn connect(globalThis: *JSC.JSGlobalObject, db: *PostgresSQLDatabase, options: ConnectionOptions) !void {
+ autoRegister(globalThis);
+ log("connect {s}:{d}", .{ options.tcp.hostname, options.tcp.port });
+ const socket = Socket.connectAnon(
+ options.tcp.hostname,
+ options.tcp.port,
+ globalThis.bunVM().rareData().postgres_data.tcp_ctx.?,
+ &db.connection,
+ ) orelse {
+ return error.FailedToConnect;
+ };
+ db.connection.tcp = socket;
+ }
+
+ pub fn closeGracefully(this: *PostgresConnection) void {
+ this.tcp.close(0, null); // todo
+ }
+
+ pub fn autoRegister(global: *JSC.JSGlobalObject) void {
+ var vm = global.bunVM();
+
+ if (vm.rareData().postgres_data.tcp_ctx == null) {
+ var opts: uws.us_socket_context_options_t = undefined;
+ @memset(@ptrCast([*]u8, &opts), 0, @sizeOf(uws.us_socket_context_options_t));
+ var ctx = uws.us_create_socket_context(0, vm.uws_event_loop.?, @sizeOf(usize), opts).?;
+ vm.rareData().postgres_data.tcp_ctx = ctx;
+ Socket.configure(
+ ctx,
+ false,
+ PostgresConnection,
+ struct {
+ pub const onClose = PostgresConnection.onClose;
+ pub const onData = PostgresConnection.onData;
+ pub const onWritable = PostgresConnection.onWritable;
+ pub const onTimeout = PostgresConnection.onTimeout;
+ pub const onConnectError = PostgresConnection.onConnectError;
+ pub const onEnd = PostgresConnection.onEnd;
+ },
+ );
+ }
+ }
+
+ pub inline fn database(this: *PostgresConnection) *PostgresSQLDatabase {
+ return @fieldParentPtr(PostgresSQLDatabase, "connection", this);
+ }
+
+ pub fn onWritable(
+ this: *PostgresConnection,
+ socket: Socket,
+ ) void {
+ std.debug.assert(socket.socket == this.tcp.socket);
+ // if (this.to_send.len == 0)
+ // return;
+
+ // const wrote = socket.write(this.to_send, true);
+ // if (wrote < 0) {
+ // this.terminate(ErrorCode.failed_to_write);
+ // return;
+ // }
+ // this.to_send = this.to_send[@min(@intCast(usize, wrote), this.to_send.len)..];
+ }
+ pub fn onTimeout(
+ this: *PostgresConnection,
+ _: Socket,
+ ) void {
+ this.terminate(ErrorCode.timeout);
+ }
+ pub fn onConnectError(this: *PostgresConnection, _: Socket, _: c_int) void {
+ this.terminate(ErrorCode.failed_to_connect);
+ }
+
+ pub fn onEnd(this: *PostgresConnection, socket: Socket) void {
+ log("onEnd", .{});
+ std.debug.assert(socket.socket == this.tcp.socket);
+ this.terminate(ErrorCode.ended);
+ }
+
+ pub fn onData(_: *PostgresConnection, _: Socket, data: []const u8) void {
+ log("onData: {d}", .{data.len});
+ }
+
+ pub fn onClose(this: *PostgresConnection, _: Socket, _: c_int, _: ?*anyopaque) void {
+ log("onClose", .{});
+ this.terminate(ErrorCode.closed);
+ }
+
+ pub fn onOpen(this: *PostgresConnection, socket: Socket) void {
+ log("onOpen", .{});
+ std.debug.assert(socket.socket == this.tcp.socket);
+ }
+
+ pub fn terminate(this: *PostgresConnection, code: ErrorCode) void {
+ log("terminate - {s}", .{code.code()});
+ this.poll_ref.disable();
+
+ if (this.tcp.isEstablished() and !this.tcp.isClosed()) {
+ this.tcp.ext(?*anyopaque).?.* = null;
+ this.tcp.close(0, null);
+ }
+
+ this.database().terminate(code);
+ }
+};
+
+const PendingQuery = struct {
+ resolve: JSC.JSValue,
+ reject: JSC.JSValue,
+ query: JSC.ZigString.Slice,
+};
+
+pub const PostgresSQLDatabase = struct {
+ const log = Output.scoped(.PostgresSQLDatabase, false);
+ pub usingnamespace JSC.Codegen.JSPostgresSQLDatabase;
+ arena: std.heap.ArenaAllocator,
+ connection: PostgresConnection,
+ options: ConnectionOptions,
+ this_jsvalue: JSC.JSValue = .zero,
+ globalObject: *JSC.JSGlobalObject,
+ status: Status = .connecting,
+ has_pending_activity: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(false),
+
+ close_status: ErrorCode = .unknown,
+
+ pending_queries: std.ArrayListUnmanaged(PendingQuery) = .{},
+
+ pub const Status = enum {
+ connecting,
+ connected,
+ closing,
+ closed,
+
+ pub const label = bun.enumMap(Status, .{
+ .{ .connecting, "connecting" },
+ .{ .connected, "connected" },
+ .{ .closing, "closing" },
+ .{ .closed, "closed" },
+ });
+ };
+
+ pub fn hasPendingActivity(this: *PostgresSQLDatabase) callconv(.C) bool {
+ @fence(.Acquire);
+ return this.has_pending_activity.load(.Acquire);
+ }
+
+ pub fn getStatus(this: *PostgresSQLDatabase, globalThis: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
+ return JSC.ZigString.init(this.status.label()).toValueGC(globalThis);
+ }
+
+ fn setStatus(
+ this: *PostgresSQLDatabase,
+ status: Status,
+ _: JSC.JSValue,
+ ) void {
+ this.status = status;
+ this.updateHasPendingData();
+ if (status == .connected) {}
+ }
+
+ pub fn updateHasPendingData(this: *PostgresSQLDatabase) void {
+ @fence(.Release);
+ this.has_pending_activity.store(this.status != .closed, .Release);
+ }
+
+ pub fn terminate(this: *PostgresSQLDatabase, code: ErrorCode) void {
+ const js_value = this.this_jsvalue;
+ if (this.status == .connecting) {
+ this.setStatus(.closed, js_value);
+ return;
+ }
+ this.close_status = code;
+
+ if (this.status == .closed)
+ return;
+
+ this.setStatus(.closed, js_value);
+ }
+
+ pub fn connect(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ const arguments_ = callframe.arguments(8);
+ const arguments: []const JSC.JSValue = arguments_.ptr[0..arguments_.len];
+
+ if (arguments.len < 1) {
+ globalObject.throwNotEnoughArguments("connect", 1, 0);
+ return .zero;
+ }
+
+ if (arguments[0].isEmptyOrUndefinedOrNull()) {
+ globalObject.throwInvalidArgumentType("connect", "options", "url string or object");
+ return .zero;
+ }
+
+ var arena = std.heap.ArenaAllocator.init(globalObject.allocator());
+
+ var options = ConnectionOptions{ .tcp = .{} };
+
+ if (arguments[0].get(globalObject, "host")) |value| {
+ if (!value.isEmptyOrUndefinedOrNull()) {
+ const str = value.toSlice(globalObject, arena.allocator()).clone(arena.allocator()) catch @panic("Out of memory");
+ if (str.len > 0)
+ options.tcp.hostname = str.slice();
+ }
+ }
+ if (arguments[0].get(globalObject, "port")) |value| {
+ if (!value.isEmptyOrUndefinedOrNull()) {
+ const str = value.toSlice(globalObject, arena.allocator()).clone(arena.allocator()) catch @panic("Out of memory");
+ if (str.len > 0)
+ options.tcp.port = std.fmt.parseInt(u16, str.slice(), 10) catch @panic("Error parsing port number");
+ }
+ }
+ if (arguments[0].get(globalObject, "database")) |value| {
+ if (!value.isEmptyOrUndefinedOrNull()) {
+ const str = value.toSlice(globalObject, arena.allocator()).clone(arena.allocator()) catch @panic("Out of memory");
+ if (str.len > 0)
+ options.tcp.database = str.slice();
+ }
+ }
+ if (arguments[0].get(globalObject, "user")) |value| {
+ if (!value.isEmptyOrUndefinedOrNull()) {
+ const str = value.toSlice(globalObject, arena.allocator()).clone(arena.allocator()) catch @panic("Out of memory");
+ if (str.len > 0)
+ options.tcp.user = str.slice();
+ }
+ }
+ if (arguments[0].get(globalObject, "pass")) |value| {
+ if (!value.isEmptyOrUndefinedOrNull()) {
+ const str = value.toSlice(globalObject, arena.allocator()).clone(arena.allocator()) catch @panic("Out of memory");
+ if (str.len > 0)
+ options.tcp.password = str.slice();
+ }
+ }
+ // if (arguments[0].get(globalObject, "path")) |value| {
+ // if (!value.isEmptyOrUndefinedOrNull()) {
+ // const str = value.toSlice(globalObject).clone(arena.allocator());
+ // if (str.len > 0)
+ // options.tcp.p = str.slice();
+ // }
+ // }
+ var db = globalObject.allocator().create(PostgresSQLDatabase) catch |err| {
+ arena.deinit();
+ globalObject.throwError(err, "failed to allocate db");
+ return .zero;
+ };
+
+ const this = db.toJS(globalObject);
+ db.* = .{
+ .this_jsvalue = this,
+ .options = options,
+ .status = .connecting,
+ .arena = arena,
+ .globalObject = globalObject,
+ .connection = undefined,
+ };
+ PostgresSQLDatabase.onCloseSetCached(this, globalObject, arguments[0].get(globalObject, "onClose") orelse @panic("Expected onClose. Don't call this function outside of bun:sql."));
+ PostgresSQLDatabase.onNoticeSetCached(this, globalObject, arguments[0].get(globalObject, "onNotice") orelse @panic("Expected onNotice. Don't call this function outside of bun:sql."));
+ PostgresSQLDatabase.onOpenSetCached(this, globalObject, arguments[0].get(globalObject, "onOpen") orelse @panic("Expected onOpen. Don't call this function outside of bun:sql."));
+ PostgresSQLDatabase.onTimeoutSetCached(this, globalObject, arguments[0].get(globalObject, "onTimeout") orelse @panic("Expected onTimeout. Don't call this function outside of bun:sql."));
+ PostgresSQLDatabase.onDrainSetCached(this, globalObject, arguments[0].get(globalObject, "onDrain") orelse @panic("Expected onDrain. Don't call this function outside of bun:sql."));
+ db.updateHasPendingData();
+ PostgresConnection.connect(globalObject, db, options) catch |err| {
+ arena.deinit();
+ globalObject.throwError(err, "failed to connect");
+ return .zero;
+ };
+
+ return this;
+ }
+
+ pub fn query(_: *PostgresSQLDatabase, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ return JSC.JSValue.jsUndefined();
+ }
+
+ pub fn ref(_: *PostgresSQLDatabase, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ return JSC.JSValue.jsUndefined();
+ }
+
+ pub fn unref(_: *PostgresSQLDatabase, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ return JSC.JSValue.jsUndefined();
+ }
+
+ pub fn close(this: *PostgresSQLDatabase, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ if (this.status == .closed) {
+ return JSC.ZigString.init(this.close_status.label()).toValueGC(globalObject);
+ }
+
+ if (this.status == .closing) {
+ return JSC.JSValue.jsUndefined();
+ }
+
+ std.debug.assert(!this.connection.tcp.isClosed());
+ std.debug.assert(this.connection.tcp.isEstablished());
+ std.debug.assert(!this.connection.tcp.isShutdown());
+
+ this.setStatus(.closing, this.this_jsvalue);
+ this.connection.closeGracefully();
+ return JSC.JSValue.jsUndefined();
+ }
+
+ pub fn finalize(this: *PostgresSQLDatabase) callconv(.C) void {
+ this.deinit();
+ }
+
+ pub fn deinit(this: *PostgresSQLDatabase) void {
+ std.debug.assert(this.status == .closed);
+ this.arena.deinit();
+ bun.default_allocator.destroy(this);
+ }
+};
+
+comptime {
+ @export(PostgresSQLDatabase.connect, .{
+ .name = "Bun__PostgreSQL__connect",
+ });
+}
diff --git a/src/bun.js/api/postgres_messages.zig b/src/bun.js/api/postgres_messages.zig
new file mode 100644
index 000000000..35fee4906
--- /dev/null
+++ b/src/bun.js/api/postgres_messages.zig
@@ -0,0 +1,410 @@
+pub const String = extern struct {
+ data: [*:0]u8 align(1) = undefined,
+};
+
+pub const Byten = extern struct {
+ data: [*:0]u8 align(1) = undefined,
+};
+
+pub const AuthenticationOk = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ byte1: u8 align(1) = 'R',
+ length: i32 align(1) = 8,
+ tag: i32 align(1) = 0,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationKerberosV5 = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ byte1: u8 align(1) = 'R',
+ length: i32 align(1) = 8,
+ tag: i32 align(1) = 2,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationMD5Password = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ byte1: u8 align(1) = 'R',
+ length: i32 align(1) = 12,
+ tag: i32 align(1) = 5,
+ salt: [4]u8 align(1) = undefined,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationSCMCredential = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ byte1: u8 align(1) = 'R',
+ length: i32 align(1) = 8,
+ tag: i32 align(1) = 6,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationCleartextPassword = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ byte1: u8 align(1) = 'R',
+ length: i32 align(1) = 8,
+ tag: i32 align(1) = 3,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationGSS = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an authentication request.
+ byte1: u8 align(1) = 'R',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 8,
+ /// Specifies that GSSAPI authentication is required.
+ tag: i32 align(1) = 7,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationGSSContinue = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an authentication request.
+ byte1: u8 align(1) = 'R',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 8,
+ /// Specifies that this message contains GSSAPI or SSPI data.
+ tag: i32 align(1) = 8,
+ /// GSSAPI or SSPI authentication data.
+ data: []u8 align(1),
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationSSPI = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an authentication request.
+ byte1: u8 align(1) = 'R',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 8,
+ /// Specifies that SSPI authentication is required.
+ tag: i32 align(1) = 9,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationSASL = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an authentication request.
+ byte1: u8 align(1) = 'R',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 8,
+ /// Specifies that SASL authentication is required.
+ tag: i32 align(1) = 10,
+ /// The message body is a list of SASL authentication mechanisms, in the server's order of preference. A zero byte is required as terminator after the last authentication mechanism name. For each mechanism, there is the following:
+ mechanisms: [:0]const u8 align(1) = "",
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationSASLContinue = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an authentication request.
+ byte1: u8 align(1) = 'R',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// Specifies that this message contains a SASL challenge.
+ tag: i32 align(1) = 11,
+ /// SASL data, specific to the SASL mechanism being used.
+ data: []const u8 align(1) = "",
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const AuthenticationSASLFinal = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an authentication request.
+ byte1: u8 align(1) = 'R',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// Specifies that SASL authentication has completed.
+ tag: i32 align(1) = 12,
+ /// SASL outcome "additional data", specific to the SASL mechanism being used.
+ data: []const u8 align(1) = "",
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CancelRequest = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 16,
+ /// The cancel request code. The value is chosen to contain 1234 in the most significant 16 bits, and 5678 in the least significant 16 bits. (To avoid confusion, this code must not be the same as any protocol version number.)
+ code: i32 align(1) = 80877102,
+ /// The process ID of the target backend.
+ process_id: i32 align(1),
+ /// The secret key for the target backend.
+ secret_key: i32 align(1),
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const Close = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a Close command.
+ byte1: u8 align(1) = 'C',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1),
+ /// 'S' to close a prepared statement; or 'P' to close a portal.
+ type: u8 align(1),
+ /// The name of the prepared statement or portal to close (an empty string selects the unnamed prepared statement or portal).
+ name: []u8 align(1),
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CloseComplete = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a Close-complete indicator.
+ byte1: u8 align(1) = '3',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 4,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CommandComplete = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a command-completed response.
+ byte1: u8 align(1) = 'C',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// The command tag. This is usually a single word that identifies which SQL command was completed.
+ /// For an INSERT command, the tag is INSERT oid rows, where rows is the number of rows inserted. oid used to be the object ID of the inserted row if rows was 1 and the target table had OIDs, but OIDs system columns are not supported anymore; therefore oid is always 0.
+ /// For a DELETE command, the tag is DELETE rows where rows is the number of rows deleted.
+ /// For an UPDATE command, the tag is UPDATE rows where rows is the number of rows updated.
+ /// For a SELECT or CREATE TABLE AS command, the tag is SELECT rows where rows is the number of rows retrieved.
+ /// For a MOVE command, the tag is MOVE rows where rows is the number of rows the cursor's position has been changed by.
+ /// For a FETCH command, the tag is FETCH rows where rows is the number of rows that have been retrieved from the cursor.
+ /// For a COPY command, the tag is COPY rows where rows is the number of rows copied. (Note: the row count appears only in PostgreSQL 8.2 and later.)
+ tag: [8]u8 align(1) = .{ 0, 0, 0, 0, 0, 0, 0, 0 },
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CopyData = extern struct {
+ pub const direction = .both;
+ pub const Bytes = extern struct {
+ /// Identifies the message as copy data.
+ byte1: u8 align(1) = 'd',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// Data that forms part of a copy-in or copy-out operation.
+ data: []const u8 align(1),
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CopyDone = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as copy-done.
+ byte1: u8 align(1) = 'c',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 4,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CopyFail = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as copy-fail.
+ byte1: u8 align(1) = 'f',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// The failure message.
+ message: [:0]const u8 align(1) = "",
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CopyInResponse = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a CopyInResponse.
+ byte1: u8 align(1) = 'G',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+
+ /// 0 indicates the overall COPY format is textual (rows separated by newlines, columns separated by separator characters, etc.). 1 indicates the overall copy format is binary (similar to DataRow format). See COPY for more information.
+ copy_format: i8 align(1) = undefined,
+
+ /// The format code being used for the data transfer.
+ columns_count: i16 align(1) = 0,
+
+ /// The format codes to be used for each column. Each must presently be zero (text) or one (binary). All must be zero if the overall copy format is textual.
+ columns: []i16 align(1) = &[_]i16{},
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CopyOutResponse = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a CopyOutResponse.
+ byte1: u8 align(1) = 'H',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+
+ /// 0 indicates the overall COPY format is textual (rows separated by newlines, columns separated by separator characters, etc.). 1 indicates the overall copy format is binary (similar to DataRow format). See COPY for more information.
+ copy_format: i8 align(1) = undefined,
+
+ /// The format code being used for the data transfer.
+ columns_count: i16 align(1) = 0,
+
+ /// The format codes to be used for each column. Each must presently be zero (text) or one (binary). All must be zero if the overall copy format is textual.
+ columns: []i16 align(1) = &[_]i16{},
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const CopyBothResponse = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = struct {
+ /// Identifies the message as a CopyBothResponse.
+ byte1: u8 align(1) = 'W',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+
+ /// 0 indicates the overall COPY format is textual (rows separated by newlines, columns separated by separator characters, etc.). 1 indicates the overall copy format is binary (similar to DataRow format). See COPY for more information.
+ copy_format: i8 align(1) = undefined,
+
+ /// The format code being used for the data transfer.
+ columns_count: i16 align(1) = 0,
+
+ /// The format codes to be used for each column. Each must presently be zero (text) or one (binary). All must be zero if the overall copy format is textual.
+ columns: [*]i16 align(1) = &[_]i16{},
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const DataRow = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a DataRow.
+ byte1: u8 align(1) = 'D',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// The number of column values that follow (possibly zero).
+ columns_count: i16 align(1) = 0,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const Describe = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a Describe command.
+ byte1: u8 align(1) = 'D',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// The object to describe. 'S' to describe a prepared statement; or 'P' to describe a portal.
+ object_type: u8 align(1) = 'S',
+ /// The name of the prepared statement or portal to describe (an empty string selects the unnamed prepared statement or portal).
+ object_name: [*:0]const u8 align(1) = "",
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const EmptyQueryResponse = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an EmptyQueryResponse.
+ byte1: u8 align(1) = 'I',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 4,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const ErrorResponse = extern struct {
+ pub const direction = .backend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an ErrorResponse.
+ byte1: u8 align(1) = 'E',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// The fields of the error response.
+ fields: [*:0]const Field align(1) = &[_]Field{},
+ };
+ pub const bytes = Bytes{};
+
+ pub const Field = extern struct {
+ /// The error code.
+ code: u8 align(1) = undefined,
+ /// The error message.
+ message: [:0]const u8 align(1) = "",
+ };
+};
+
+pub const Execute = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as an Execute command.
+ byte1: u8 align(1) = 'E',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// The name of the portal to execute (an empty string selects the unnamed portal).
+ portal_name: [*:0]const u8 align(1) = "",
+ /// The maximum number of rows to return, if portal contains a query that returns rows (ignored otherwise). Zero denotes "no limit".
+ max_rows: i32 align(1) = 0,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const Flush = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a Flush.
+ byte1: u8 align(1) = 'H',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = 4,
+ };
+ pub const bytes = Bytes{};
+};
+
+pub const FunctionCall = extern struct {
+ pub const direction = .frontend;
+ pub const Bytes = extern struct {
+ /// Identifies the message as a FunctionCall.
+ byte1: u8 align(1) = 'F',
+ /// Length of message contents in bytes, including self.
+ length: i32 align(1) = undefined,
+ /// The object ID of the function to be called.
+ function_id: i32 align(1) = undefined,
+
+ /// The number of argument format codes that follow (denoted C below). This can be zero to indicate that there are no arguments or that the arguments all use the default format (text); or one, in which case the specified format code is applied to all arguments; or it can equal the actual number of arguments.
+ format_codes_len: i16 align(1) = 0,
+
+ format_codes: [:0]i16 align(1) = &[_]i16{},
+ };
+ pub const bytes = Bytes{};
+
+ pub const Argument = extern struct {
+ /// The argument value, in the format indicated by the associated format code. n is the above length.
+ value: [:0]const u8 align(1) = "",
+ };
+};