aboutsummaryrefslogtreecommitdiff
path: root/src/proto/row_description.zig
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-09-26 06:51:06 +0100
committerMartin Ashby <martin@ashbysoft.com>2023-09-26 06:51:06 +0100
commit183d60a6e87230cc767c56900b94c9c694596de1 (patch)
treec08b473a293dc465989a09c5d681191898cf2c2f /src/proto/row_description.zig
parent02f9e99bfccad8837d327880f756ec7bab711783 (diff)
downloadpgz-183d60a6e87230cc767c56900b94c9c694596de1.tar.gz
pgz-183d60a6e87230cc767c56900b94c9c694596de1.tar.bz2
pgz-183d60a6e87230cc767c56900b94c9c694596de1.tar.xz
pgz-183d60a6e87230cc767c56900b94c9c694596de1.zip
Move protocol definitions into a subfolder
Diffstat (limited to 'src/proto/row_description.zig')
-rw-r--r--src/proto/row_description.zig136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/proto/row_description.zig b/src/proto/row_description.zig
new file mode 100644
index 0000000..a0e8810
--- /dev/null
+++ b/src/proto/row_description.zig
@@ -0,0 +1,136 @@
+const std = @import("std");
+const log = std.log.scoped(.pgz);
+const ByteArrayList = std.ArrayList(u8);
+const ProtocolError = @import("../main.zig").ProtocolError;
+const ClientError = @import("../main.zig").ClientError;
+const enum_from_int = @import("../main.zig").enum_from_int;
+const FormatCode = @import("../main.zig").FormatCode;
+
+pub const Tag: u8 = 'T';
+
+const RowDescription = @This();
+
+buf: ?[]const u8 = null, // owned
+fields: ?[]Field = null, // owned
+
+pub const Field = struct {
+ name: []const u8,
+ table_oid: u32,
+ attr_no: u16,
+ data_type_oid: u32,
+ data_type_size: i16,
+ data_type_modifier: u32,
+ format_code: FormatCode,
+};
+
+pub fn read(a: std.mem.Allocator, b: []const u8) !RowDescription {
+ var res: RowDescription = undefined;
+ res.buf = try a.dupe(u8, b);
+ errdefer res.deinit(a);
+ var fbs = std.io.fixedBufferStream(res.buf.?);
+ var reader = fbs.reader();
+ const n_fields = try reader.readIntBig(u16);
+ res.fields = try a.alloc(Field, n_fields);
+ for (0..n_fields) |i| {
+ const name_start = fbs.pos;
+ try reader.skipUntilDelimiterOrEof(0);
+ const name_end = fbs.pos-1;
+ const name = res.buf.?[name_start..name_end];
+ const field = Field{
+ .name = name,
+ .table_oid = try reader.readIntBig(u32),
+ .attr_no = try reader.readIntBig(u16),
+ .data_type_oid = try reader.readIntBig(u32),
+ .data_type_size = try reader.readIntBig(i16),
+ .data_type_modifier = try reader.readIntBig(u32),
+ .format_code = enum_from_int(FormatCode, try reader.readIntBig(u16)) orelse return ProtocolError.InvalidFormatCode,
+ };
+ res.fields.?[i] = field;
+ }
+ return res;
+}
+
+pub fn write(self: RowDescription, a: std.mem.Allocator, stream_writer: anytype) !void {
+ try stream_writer.writeByte(Tag);
+ var al = ByteArrayList.init(a);
+ defer al.deinit();
+ var cw = std.io.countingWriter(al.writer());
+ var writer = cw.writer();
+ try writer.writeIntBig(u32, 0); // length placeholder
+ try writer.writeIntBig(u16, @as(u16, @intCast(self.fields.?.len)));
+ for (self.fields.?) |field| {
+ try writer.writeAll(field.name);
+ try writer.writeByte(0);
+ try writer.writeIntBig(u32, field.table_oid);
+ try writer.writeIntBig(u16, field.attr_no);
+ try writer.writeIntBig(u32, field.data_type_oid);
+ try writer.writeIntBig(i16, field.data_type_size);
+ try writer.writeIntBig(u32, field.data_type_modifier);
+ try writer.writeIntBig(u16, @intFromEnum(field.format_code));
+ }
+ std.mem.writeIntBig(u32, al.items[0..4], @as(u32, @intCast(cw.bytes_written)));
+ try stream_writer.writeAll(al.items);
+}
+
+pub fn deinit(self: *RowDescription, a: std.mem.Allocator) void {
+ if (self.fields != null) a.free(self.fields.?);
+ if (self.buf != null) a.free(self.buf.?);
+}
+
+test "round trip" {
+ const allocator = std.testing.allocator;
+ var fields = try allocator.alloc(Field, 3);
+ fields[0] = .{
+ .name = "foo",
+ .table_oid = 1,
+ .attr_no = 2,
+ .data_type_oid = 3,
+ .data_type_size = 4,
+ .data_type_modifier = 5,
+ .format_code = .Binary,
+ };
+ fields[1] = .{
+ .name = "bar",
+ .table_oid = 1,
+ .attr_no = 2,
+ .data_type_oid = 3,
+ .data_type_size = 4,
+ .data_type_modifier = 5,
+ .format_code = .Binary,
+ };
+ fields[2] = .{
+ .name = "BAZZZZZ",
+ .table_oid = 99,
+ .attr_no = 98,
+ .data_type_oid = 97,
+ .data_type_size = 96,
+ .data_type_modifier = 95,
+ .format_code = .Text,
+ };
+ var f0 = fields[0];
+ var f1 = fields[1];
+ var f2 = fields[2];
+ var sm = RowDescription{
+ .fields = fields,
+ };
+ 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 RowDescription.read(allocator, buf);
+ defer sm2.deinit(allocator);
+
+ try std.testing.expectEqualDeep(f0, sm2.fields.?[0]);
+ try std.testing.expectEqualDeep(f1, sm2.fields.?[1]);
+ try std.testing.expectEqualDeep(f2, sm2.fields.?[2]);
+}