aboutsummaryrefslogtreecommitdiff
path: root/src/error_response.zig
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-09-23 15:18:38 +0100
committerMartin Ashby <martin@ashbysoft.com>2023-09-23 15:18:38 +0100
commit5a91b37ee7dd36db52dfde1727b780ec3fa4c67d (patch)
treed17a1a8449ecd48fcf67f93edf7ba16c99974e6a /src/error_response.zig
parentc3c45287396569a96107c9b1413ac181f7197a6e (diff)
downloadpgz-5a91b37ee7dd36db52dfde1727b780ec3fa4c67d.tar.gz
pgz-5a91b37ee7dd36db52dfde1727b780ec3fa4c67d.tar.bz2
pgz-5a91b37ee7dd36db52dfde1727b780ec3fa4c67d.tar.xz
pgz-5a91b37ee7dd36db52dfde1727b780ec3fa4c67d.zip
Add error_response
Start adding connection abstraction
Diffstat (limited to 'src/error_response.zig')
-rw-r--r--src/error_response.zig170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/error_response.zig b/src/error_response.zig
new file mode 100644
index 0000000..4d99ba4
--- /dev/null
+++ b/src/error_response.zig
@@ -0,0 +1,170 @@
+const std = @import("std");
+const HMByteString = std.AutoHashMap(u8, []const u8);
+const ByteArrayList = std.ArrayList(u8);
+const ProtocolError = @import("main.zig").ProtocolError;
+
+const ErrorResponse = @This();
+const Tag: u8 = 'E';
+
+buf: ?[]const u8 = null, // owned
+severity: []const u8,
+severity_unlocalized: ?[]const u8 = null,
+code: []const u8,
+message: []const u8,
+detail: ?[]const u8 = null,
+hint: ?[]const u8 = null,
+position: ?u32 = null,
+internal_position: ?u32 = null,
+internal_query: ?[]const u8 = null,
+where: ?[]const u8 = null,
+schema_name: ?[]const u8 = null,
+table_name: ?[]const u8 = null,
+column_name: ?[]const u8 = null,
+data_type_name: ?[]const u8 = null,
+constraint_name: ?[]const u8 = null,
+file_name: ?[]const u8 = null,
+line: ?u32 = null,
+routine: ?[]const u8 = null,
+unknown_fields: HMByteString,
+
+pub fn read(allocator: std.mem.Allocator, b: []const u8) !ErrorResponse {
+ var res: ErrorResponse = undefined;
+ res.unknown_fields = HMByteString.init(allocator);
+ res.buf = try allocator.dupe(u8, b);
+ errdefer allocator.free(res.buf.?);
+ var it = std.mem.splitScalar(u8, res.buf.?, 0);
+ var setSev = false; var setCode = false; var setMsg = false;
+ while (it.next()) |next| {
+ if (next.len < 1) break;
+ switch (next[0]) {
+ 0 => break,
+ 'S' => {
+ res.severity = next[1..];
+ setSev = true;
+ },
+ 'V' => {
+ res.severity_unlocalized = next[1..];
+ },
+ 'C' => {
+ res.code = next[1..];
+ setCode = true;
+ },
+ 'M' => {
+ res.message = next[1..];
+ setMsg = true;
+ },
+ 'D' => {
+ res.detail = next[1..];
+ },
+ 'H' => {
+ res.hint = next[1..];
+ },
+ 'P' => {
+ res.position = try std.fmt.parseInt(u32, next[1..], 10);
+ },
+ 'p' => {
+ res.internal_position = try std.fmt.parseInt(u32, next[1..], 10);
+ },
+ 'q' => {
+ res.internal_query = next[1..];
+ },
+ 'W' => {
+ res.where = next[1..];
+ },
+ 's' => {
+ res.schema_name = next[1..];
+ },
+ 't' => {
+ res.table_name = next[1..];
+ },
+ 'c' => {
+ res.column_name = next[1..];
+ },
+ 'd' => {
+ res.data_type_name = next[1..];
+ },
+ 'n' => {
+ res.constraint_name = next[1..];
+ },
+ 'F' => {
+ res.file_name = next[1..];
+ },
+ 'L' => {
+ res.line = try std.fmt.parseInt(u32, next[1..], 10);
+ },
+ 'R' => {
+ res.routine = next[1..];
+ },
+ else => {
+ try res.unknown_fields.put(next[0], next[1..]);
+ }
+ }
+ }
+ if (!(setSev and setCode and setMsg)) return ProtocolError.MissingField;
+ return res;
+}
+pub fn write(self: ErrorResponse, allocator: std.mem.Allocator, stream_writer: anytype) !void {
+ try stream_writer.writeByte(Tag);
+ var al = ByteArrayList.init(allocator);
+ defer al.deinit();
+ var cw = std.io.countingWriter(al.writer());
+ var writer = cw.writer();
+ try writer.writeIntBig(u32, 0); // Length placeholder.
+
+ try write_field_nt('S', self, "severity", writer);
+ if (self.severity_unlocalized) |severity_unlocalized| {
+ try write_nt('V', severity_unlocalized, writer);
+ }
+ try write_field_nt('C', self, "code", writer);
+ try write_field_nt('M', self, "message", writer);
+ // TODO rest of the fields
+
+ // replace the length and write it to the actual stream
+ std.mem.writeIntBig(u32, al.items[0..4], @as(u32, @intCast(cw.bytes_written)));
+ try stream_writer.writeAll(al.items);
+}
+fn write_field_nt(comptime tag: u8, self: ErrorResponse, comptime field: []const u8, writer: anytype) !void {
+ try write_nt(tag, @field(self, field), writer);
+}
+fn write_nt(comptime tag: u8, value: []const u8, writer: anytype) !void {
+ try writer.writeByte(tag);
+ try writer.writeAll(value);
+ try writer.writeByte(0);
+}
+
+pub fn deinit(self: *ErrorResponse, allocator: std.mem.Allocator) void {
+ self.unknown_fields.deinit();
+ if (self.buf != null) allocator.free(self.buf.?);
+}
+
+test "round trip" {
+ const allocator = std.testing.allocator;
+ var sm = ErrorResponse{
+ .severity = "foo",
+ .severity_unlocalized = "foo_unlocal",
+ .code = "bar",
+ .message = "baz",
+ .unknown_fields = HMByteString.init(allocator),
+ };
+ defer sm.deinit(allocator);
+
+ var bal = ByteArrayList.init(allocator);
+ defer bal.deinit();
+ try sm.write(allocator, bal.writer());
+
+ var fbs = std.io.fixedBufferStream(bal.items);
+ var reader = fbs.reader();
+ const tag = try reader.readByte();
+ try std.testing.expectEqual(Tag, tag);
+ const len = try reader.readIntBig(u32);
+ const buf = try allocator.alloc(u8, len - 4);
+ defer allocator.free(buf);
+ try reader.readNoEof(buf);
+ var sm2 = try ErrorResponse.read(allocator, buf);
+ defer sm2.deinit(allocator);
+
+ try std.testing.expectEqualStrings("foo", sm2.severity);
+ try std.testing.expectEqualStrings("foo_unlocal", sm2.severity_unlocalized.?);
+ try std.testing.expectEqualStrings("bar", sm2.code);
+ try std.testing.expectEqualStrings("baz", sm2.message);
+}