1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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]);
}
|