Something went wrong. Try again.
Native PostgreSQL driver / client for Zig
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411const std = @import("std");const lib = @import("lib.zig");const buffer = @import("buffer");
// These are nested inside the the Types structure so that we can generate an// oid => encoding maping. See the oidEncoding function.pub const OID = struct { decimal: i32, encoded: [4]u8,
pub fn make(decimal: i32) OID { var encoded: [4]u8 = undefined; std.mem.writeInt(i32, &encoded, decimal, .big); return .{ .decimal = decimal, .encoded = encoded, }; }};
pub const text_encoding = [2]u8{ 0, 0 };pub const binary_encoding = [2]u8{ 0, 1 };
// Any "decodeKnown" you see is just an optimization to avoid extra assertions// when decoding an individual array value. Once we know the array type, we don't// need to assert the oid of each individual value.
// Every supported type is here. This includes the format we want to// encode/decode (text or binary), and the logic for encoding and decoding.
pub const Cidr = @import("types/cidr.zig").Cidr;pub const Numeric = @import("types/numeric.zig").Numeric;
pub const Char = struct { // A blank-padded char pub const oid = OID.make(1042); const encoding = &binary_encoding;
fn encode(value: u8, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Char.encoding, format_pos); try buf.write(&.{ 0, 0, 0, 1 }); // length of our data return buf.writeByte(value); }
pub fn decode(data: []const u8, data_oid: i32) u8 { lib.assertDecodeType(u8, &.{Char.oid.decimal}, data_oid); return data[0]; }
pub fn decodeKnown(data: []const u8) u8 { return data[0]; }};
pub const Int16 = struct { pub const oid = OID.make(21); const encoding = &binary_encoding;
fn encode(value: i16, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Int16.encoding, format_pos); try buf.write(&.{ 0, 0, 0, 2 }); // length of our data return buf.writeIntBig(i16, value); }
fn encodeUnsigned(value: u16, buf: *buffer.Buffer, format_pos: usize) !void { if (value > 32767) return error.UnsignedIntWouldBeTruncated; return Int16.encode(@intCast(value), buf, format_pos); }
pub fn decode(data: []const u8, data_oid: i32) i16 { lib.assertDecodeType(i16, &.{Int16.oid.decimal}, data_oid); return Int16.decodeKnown(data); }
pub fn decodeKnown(data: []const u8) i16 { return std.mem.readInt(i16, data[0..2], .big); }};
pub const Int32 = struct { pub const oid = OID.make(23); const encoding = &binary_encoding;
fn encode(value: i32, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Int32.encoding, format_pos); try buf.write(&.{ 0, 0, 0, 4 }); // length of our data return buf.writeIntBig(i32, value); }
fn encodeUnsigned(value: u32, buf: *buffer.Buffer, format_pos: usize) !void { if (value > 2147483647) return error.UnsignedIntWouldBeTruncated; return Int32.encode(@intCast(value), buf, format_pos); }
pub fn decode(data: []const u8, data_oid: i32) i32 { lib.assertDecodeType(i32, &.{Int32.oid.decimal, Xid.oid.decimal}, data_oid); return Int32.decodeKnown(data); }
pub fn decodeKnown(data: []const u8) i32 { return std.mem.readInt(i32, data[0..4], .big); }};
pub const Int64 = struct { pub const oid = OID.make(20); const encoding = &binary_encoding;
fn encode(value: i64, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Int64.encoding, format_pos); try buf.write(&.{ 0, 0, 0, 8 }); // length of our data return buf.writeIntBig(i64, value); }
fn encodeUnsigned(value: u64, buf: *buffer.Buffer, format_pos: usize) !void { if (value > 9223372036854775807) return error.UnsignedIntWouldBeTruncated; return Int64.encode(@intCast(value), buf, format_pos); }
pub fn decode(data: []const u8, data_oid: i32) i64 { switch (data_oid) { Timestamp.oid.decimal, TimestampTz.oid.decimal => return Timestamp.decodeKnown(data), else => { lib.assertDecodeType(i64, &.{Int64.oid.decimal, PgLSN.oid.decimal, Xid8.oid.decimal}, data_oid); return Int64.decodeKnown(data); }, } }
pub fn decodeKnown(data: []const u8) i64 { return std.mem.readInt(i64, data[0..8], .big); }};
pub const Timestamp = struct { pub const oid = OID.make(1114); const encoding = &binary_encoding; const us_from_epoch_to_y2k = 946_684_800_000_000;
fn encode(value: i64, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Timestamp.encoding, format_pos); try buf.write(&.{ 0, 0, 0, 8 }); // length of our data return buf.writeIntBig(i64, value - us_from_epoch_to_y2k); }
pub fn decode(data: []const u8, data_oid: i32) i64 { lib.assertDecodeType(i64, &.{ Timestamp.oid.decimal, TimestampTz.oid.decimal }, data_oid); return std.mem.readInt(i64, data[0..8], .big) + us_from_epoch_to_y2k; }
pub fn decodeKnown(data: []const u8) i64 { return std.mem.readInt(i64, data[0..8], .big) + us_from_epoch_to_y2k; }};
pub const TimestampTz = struct { pub const oid = OID.make(1184); const encoding = &binary_encoding;};
pub const Float32 = struct { pub const oid = OID.make(700); const encoding = &binary_encoding;
fn encode(value: f32, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Float32.encoding, format_pos); try buf.write(&.{ 0, 0, 0, 4 }); // length of our data const tmp: *i32 = @constCast(@ptrCast(&value)); return buf.writeIntBig(i32, tmp.*); }
pub fn decode(data: []const u8, data_oid: i32) f32 { lib.assertDecodeType(f32, &.{Float32.oid.decimal}, data_oid); return Float32.decodeKnown(data); }
pub fn decodeKnown(data: []const u8) f32 { const n = std.mem.readInt(i32, data[0..4], .big); const tmp: *f32 = @constCast(@ptrCast(&n)); return tmp.*; }};
pub const Float64 = struct { pub const oid = OID.make(701); const encoding = &binary_encoding;
fn encode(value: f64, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Float64.encoding, format_pos);
try buf.write(&.{ 0, 0, 0, 8 }); // length of our data // not sure if this is the best option... const tmp: *i64 = @constCast(@ptrCast(&value)); return buf.writeIntBig(i64, tmp.*); }
pub fn decode(data: []const u8, data_oid: i32) f64 { switch (data_oid) { Numeric.oid.decimal => return Numeric.decode(data, data_oid).toFloat(), else => { lib.assertDecodeType(f64, &.{Float64.oid.decimal}, data_oid); return Float64.decodeKnown(data); }, } }
pub fn decodeKnown(data: []const u8) f64 { const n = std.mem.readInt(i64, data[0..8], .big); const tmp: *f64 = @constCast(@ptrCast(&n)); return tmp.*; }};
pub const Bool = struct { pub const oid = OID.make(16); const encoding = &binary_encoding;
fn encode(value: bool, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Bool.encoding, format_pos); try buf.write(&.{ 0, 0, 0, 1 }); // length of our data return buf.writeByte(if (value) 1 else 0); }
pub fn decode(data: []const u8, data_oid: i32) bool { lib.assertDecodeType(bool, &.{Bool.oid.decimal}, data_oid); return decodeKnown(data); }
pub fn decodeKnown(data: []const u8) bool { return data[0] == 1; }};
pub const String = struct { pub const oid = OID.make(25); // https://www.postgresql.org/message-id/CAMovtNoHFod2jMAKQjjxv209PCTJx5Kc66anwWvX0mEiaXwgmA%40mail.gmail.com // says using the text format for text-like things is faster. There was // some other threads that discussed solutions, but it isn't clear if it was // ever fixed. const encoding = &text_encoding;
fn encode(value: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(String.encoding, format_pos); var view = try buf.skip(4 + value.len); view.writeIntBig(i32, @intCast(value.len)); view.write(value); }};
pub const Bytea = struct { pub const oid = OID.make(17); const encoding = &binary_encoding;
fn encode(value: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(Bytea.encoding, format_pos); var view = try buf.skip(4 + value.len); view.writeIntBig(i32, @intCast(value.len)); view.write(value); }
pub fn decode(data: []const u8, data_oid: i32) []const u8 { switch (data_oid) { JSONB.oid.decimal => return JSONB.decodeKnown(data), else => return data, } }
pub fn decodeKnown(data: []const u8) []const u8 { return data; }
pub fn decodeKnownMutable(data: []const u8) []u8 { // we know the underlying []u8 is mutable, it comes from our Reader return @constCast(data); }};
pub const UUID = struct { pub const oid = OID.make(2950); const encoding = &binary_encoding;
fn encode(value: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(UUID.encoding, format_pos); var view = try buf.skip(20); view.write(&.{ 0, 0, 0, 16 }); switch (value.len) { 16 => view.write(value), 36 => view.write(&(try UUID.toBytes(value))), else => return error.InvalidUUID, } }
pub fn decode(data: []const u8, data_oid: i32) []const u8 { lib.assertDecodeType([]const u8, &.{UUID.oid.decimal}, data_oid); return data; }
const hex = "0123456789abcdef"; const encoded_pos = [16]u8{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34 }; const hex_to_nibble = [256]u8{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, };
pub fn toString(uuid: []const u8) ![36]u8 { if (uuid.len != 16) { return error.InvalidUUID; }
var out: [36]u8 = undefined; out[8] = '-'; out[13] = '-'; out[18] = '-'; out[23] = '-';
inline for (encoded_pos, 0..) |i, j| { out[i + 0] = hex[uuid[j] >> 4]; out[i + 1] = hex[uuid[j] & 0x0f]; } return out; }
pub fn toBytes(str: []const u8) ![16]u8 { if (str.len != 36 or str[8] != '-' or str[13] != '-' or str[18] != '-' or str[23] != '-') { return error.InvalidUUID; }
var out: [16]u8 = undefined; inline for (encoded_pos, 0..) |i, j| { const hi = hex_to_nibble[str[i + 0]]; const lo = hex_to_nibble[str[i + 1]]; if (hi == 0xff or lo == 0xff) { return error.InvalidUUID; } out[j] = hi << 4 | lo; } return out; }};
pub const PgLSN = struct { pub const oid = OID.make(3220); const encoding = &binary_encoding;};
pub const Xid = struct { pub const oid = OID.make(28); const encoding = &binary_encoding;};
pub const Xid8 = struct { pub const oid = OID.make(5069); const encoding = &binary_encoding;};
pub const MacAddr = struct { pub const oid = OID.make(829); const encoding = &binary_encoding;
fn encode(value: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { if (value.len != 6) { // assume this is a text representation return String.encode(value, buf, format_pos); } buf.writeAt(MacAddr.encoding, format_pos); var view = try buf.skip(4 + value.len); view.writeIntBig(i32, @intCast(value.len)); view.write(value); }};
pub const MacAddr8 = struct { pub const oid = OID.make(774); const encoding = &binary_encoding;
fn encode(value: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { if (value.len != 8) { // assume this is a text representation return String.encode(value, buf, format_pos); } buf.writeAt(MacAddr8.encoding, format_pos); var view = try buf.skip(4 + value.len); view.writeIntBig(i32, @intCast(value.len)); view.write(value); }};
pub const JSON = struct { pub const oid = OID.make(114); const encoding = &binary_encoding;
fn encodeBytes(value: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(JSON.encoding, format_pos); var view = try buf.skip(4 + value.len); view.writeIntBig(i32, @intCast(value.len)); view.write(value); }
fn encode(value: anytype, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(JSON.encoding, format_pos); const state = try Encode.variableLengthStart(buf); try std.json.stringify(value, .{}, buf.writer()); Encode.variableLengthFill(buf, state); }};
pub const JSONB = struct { pub const oid = OID.make(3802); const encoding = &binary_encoding;
fn encodeBytes(value: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(JSONB.encoding, format_pos); var view = try buf.skip(5 + value.len); // + 1 for the version view.writeIntBig(i32, @intCast(value.len + 1)); view.writeByte(1); // jsonb version view.write(value); }
fn encode(value: anytype, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(JSON.encoding, format_pos); const state = try Encode.variableLengthStart(buf); try buf.writeByte(1); // jsonb version try std.json.stringify(value, .{}, buf.writer()); Encode.variableLengthFill(buf, state); }
fn decode(data: []const u8, data_oid: i32) []const u8 { lib.assertDecodeType([]const u8, &.{JSONB.oid.decimal}, data_oid); return JSONB.decodeKnown(data); }
pub fn decodeKnown(data: []const u8) []const u8 { return data[1..]; }
pub fn decodeKnownMutable(data: []const u8) []u8 { // we know the underlying []u8 is mutable, it comes from our Reader return @constCast(data[1..]); }};
pub const Int16Array = struct { pub const oid = OID.make(1005); const encoding = &binary_encoding;
fn encode(values: []const i16, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Int16.oid.encoded, oid_pos); return Encode.writeIntArray(i16, 2, values, buf); }
fn encodeUnsigned(values: []const u16, buf: *buffer.Buffer, oid_pos: usize) !void { for (values) |v| { if (v > 32767) return error.UnsignedIntWouldBeTruncated; } buf.writeAt(&Int16.oid.encoded, oid_pos); return Encode.writeIntArray(i16, 2, values, buf); }};
pub const Int32Array = struct { pub const oid = OID.make(1007); const encoding = &binary_encoding;
fn encode(values: []const i32, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Int32.oid.encoded, oid_pos); return Encode.writeIntArray(i32, 4, values, buf); }
fn encodeUnsigned(values: []const u32, buf: *buffer.Buffer, oid_pos: usize) !void { for (values) |v| { if (v > 2147483647) return error.UnsignedIntWouldBeTruncated; } buf.writeAt(&Int32.oid.encoded, oid_pos); return Encode.writeIntArray(i32, 4, values, buf); }};
pub const Int64Array = struct { pub const oid = OID.make(1016); const encoding = &binary_encoding;
fn encode(values: []const i64, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Int64.oid.encoded, oid_pos); return Encode.writeIntArray(i64, 8, values, buf); }
fn encodeUnsigned(values: []const u64, buf: *buffer.Buffer, oid_pos: usize) !void { for (values) |v| { if (v > 9223372036854775807) return error.UnsignedIntWouldBeTruncated; } buf.writeAt(&Int64.oid.encoded, oid_pos); return Encode.writeIntArray(i64, 8, values, buf); }};
pub const TimestampArray = struct { pub const oid = OID.make(1115); const encoding = &binary_encoding; const us_from_epoch_to_y2k = 946_684_800_000_000;
fn encode(values: []const i64, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Timestamp.oid.encoded, oid_pos);
// every value is 12 bytes, 4 byte length + 8 byte value var view = try buf.skip(12 * values.len); for (values) |value| { view.write(&.{ 0, 0, 0, 8 }); // length of value view.writeIntBig(i64, value - us_from_epoch_to_y2k); } }};
pub const TimestampTzArray = struct { pub const oid = OID.make(1185); const encoding = &binary_encoding;
const us_from_epoch_to_y2k = 946_684_800_000_000;
fn encode(values: []const i64, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&TimestampTz.oid.encoded, oid_pos);
// every value is 12 bytes, 4 byte length + 8 byte value var view = try buf.skip(12 * values.len); for (values) |value| { view.write(&.{ 0, 0, 0, 8 }); // length of value view.writeIntBig(i64, value - us_from_epoch_to_y2k); } }};
pub const Float32Array = struct { pub const oid = OID.make(1021); const encoding = &binary_encoding;
fn encode(values: []const f32, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Float32.oid.encoded, oid_pos);
// every value takes 8 bytes, 4 for the length, 4 for the value var view = try buf.skip(8 * values.len); for (values) |value| { view.write(&.{ 0, 0, 0, 4 }); //length const tmp: *i32 = @constCast(@ptrCast(&value)); view.writeIntBig(i32, tmp.*); } }};
pub const Float64Array = struct { pub const oid = OID.make(1022); const encoding = &binary_encoding;
fn encode(values: []const f64, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Float64.oid.encoded, oid_pos);
// every value takes 12 bytes, 4 for the length, 8 for the value var view = try buf.skip(12 * values.len); for (values) |value| { view.write(&.{ 0, 0, 0, 8 }); //length const tmp: *i64 = @constCast(@ptrCast(&value)); view.writeIntBig(i64, tmp.*); } }};
pub const BoolArray = struct { pub const oid = OID.make(1000); const encoding = &binary_encoding;
fn encode(values: []const bool, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Bool.oid.encoded, oid_pos);
// every value takes 5 bytes, 4 for the length, 1 for the value var view = try buf.skip(5 * values.len); for (values) |value| { // each value is prefixed with a 4 byte length if (value) { view.write(&.{ 0, 0, 0, 1, 1 }); } else { view.write(&.{ 0, 0, 0, 1, 0 }); } } }};
pub const NumericArray = struct { pub const oid = OID.make(1231); const encoding = &binary_encoding; fn encode(values: anytype, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Numeric.oid.encoded, oid_pos);
for (values) |value| { try Numeric.encodeBuf(value, buf); } }};
pub const CidrArray = struct { pub const oid = OID.make(651); pub const inet_oid = OID.make(1041); const encoding = &binary_encoding;};
pub const MacAddrArray = struct { pub const oid = OID.make(1040); const encoding = &binary_encoding;
fn encode(values: []const []const u8, buf: *buffer.Buffer, format_pos: usize) !void { // This has challenges. Do we have a binary representation or a text representation? // Or maybe we have a mix (maybe we shouldn't support that)? // We handle this with UUID by converting the text representation to binary // but it's harder wit MacAddr because it supports 7 different text representations // and I don't really want this library to become a text parsing library which attempts // to mimic what PostgreSQL does. // So we're going to send a text-encoded array with text values, which emans // we need to convert any binary representation to text (which is a lot easier).
// The worst-case scenario is that each value takes 17 bytes. This is the // most verbose text-encoded value. When we encode a binary value as text // we'll use the most compact (12 bytes), but we might be given a 17-byte // text-encoded value, which we'll write as-is var l: usize = 0; for (values) |v| { // binary values will be encoded in a 12-byte text representation l += if (v.len == 6) 12 else v.len; }
return Encode.writeTextEncodedArray(values, l, buf, format_pos, MacAddrArray.writeOneAsText); }
fn writeOneAsText(value: []const u8, buf: *buffer.Buffer) void { if (value.len == 6) { std.fmt.format(buf.writer(), "{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}", .{ value[0], value[1], value[2], value[3], value[4], value[5] }) catch unreachable; } else { buf.writeAssumeCapacity(value); } }};
pub const MacAddr8Array = struct { pub const oid = OID.make(775); const encoding = &binary_encoding;
fn encode(values: []const []const u8, buf: *buffer.Buffer, format_pos: usize) !void { // See comments in MacAddrArray.encode var l: usize = 0; for (values) |v| { // binary values will be encoded in a 16-byte text representation l += if (v.len == 8) 16 else v.len; }
return Encode.writeTextEncodedArray(values, l, buf, format_pos, MacAddr8Array.writeOneAsText); }
fn writeOneAsText(value: []const u8, buf: *buffer.Buffer) void { if (value.len == 8) { std.fmt.format(buf.writer(), "{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}", .{ value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7] }) catch unreachable; } else { buf.writeAssumeCapacity(value); } }};
pub const ByteaArray = struct { pub const oid = OID.make(1001); const encoding = &binary_encoding;
fn encode(values: []const []const u8, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Bytea.oid.encoded, oid_pos); return Encode.writeByteArray(values, buf); }};
pub const StringArray = struct { pub const oid = OID.make(1009); const encoding = &binary_encoding;
fn encode(values: []const []const u8, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&String.oid.encoded, oid_pos); return Encode.writeByteArray(values, buf); }
fn encodeEnum(values: anytype, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&String.oid.encoded, oid_pos); for (values.*) |value| { const str = @tagName(value); try buf.writeIntBig(i32, @intCast(str.len)); try buf.write(str); } }};
pub const UUIDArray = struct { pub const oid = OID.make(2951); const encoding = &binary_encoding;
fn encode(values: []const []const u8, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&UUID.oid.encoded, oid_pos);
// every value is 20 bytes, 4 byte length + 16 byte value var view = try buf.skip(20 * values.len); for (values) |value| { view.write(&.{ 0, 0, 0, 16 }); // length of value switch (value.len) { 16 => view.write(value), 36 => view.write(&(try UUID.toBytes(value))), else => return error.InvalidUUID, } } }};
pub const JSONArray = struct { pub const oid = OID.make(199); const encoding = &binary_encoding;
fn encode(values: []const []const u8, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&JSON.oid.encoded, oid_pos); return Encode.writeByteArray(values, buf); }};
pub const JSONBArray = struct { pub const oid = OID.make(3807); const encoding = &binary_encoding;
fn encode(values: []const []const u8, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&JSONB.oid.encoded, oid_pos);
// every value has a 5 byte prefix, a 4 byte length and a 1 byte version var len = values.len * 5; for (values) |value| { len += value.len; }
var view = try buf.skip(len); for (values) |value| { // + 1 for the version view.writeIntBig(i32, @intCast(value.len + 1)); view.writeByte(1); // version view.write(value); } }};
pub const CharArray = struct { pub const oid = OID.make(1014); const encoding = &binary_encoding;
// This is for a char[] bound to a []u8 fn encodeOne(values: []const u8, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Char.oid.encoded, oid_pos);
// every value has a 5 byte prefix, a 4 byte length and a 1 byte char const len = values.len * 5; var view = try buf.skip(len); for (values) |value| { view.write(&.{ 0, 0, 0, 1 }); view.writeByte(value); } }
// This is for a char[] bound to a [][]u8 fn encode(values: []const []const u8, buf: *buffer.Buffer, oid_pos: usize) !void { buf.writeAt(&Char.oid.encoded, oid_pos); return Encode.writeByteArray(values, buf); }};
// Return the encoding we want PG to use for a particular OIDfn resultEncodingFor(oid: i32) *const [2]u8 { inline for (@typeInfo(@This()).@"struct".decls) |decl| { const S = @field(@This(), decl.name); if (@typeInfo(@TypeOf(S)) == .type and @hasField(S, "oid")) { if (oid == S.oid.decimal) { return S.encoding; } } } // default to text encoding return &binary_encoding;}
pub const Encode = struct { // helpers for encoding data (or part of the data) pub fn writeIntArray(comptime T: type, size: usize, values: []const T, buf: *buffer.Buffer) !void { var view = try buf.skip((size + 4) * values.len);
var value_len: [4]u8 = undefined; std.mem.writeInt(i32, &value_len, @intCast(size), .big); for (values) |value| { view.write(&value_len); view.writeIntBig(T, value); } }
pub fn writeByteArray(values: []const []const u8, buf: *buffer.Buffer) !void { // each value has a 4 byte length prefix var len = values.len * 4; for (values) |value| { len += value.len; }
var view = try buf.skip(len); for (values) |value| { view.writeIntBig(i32, @intCast(value.len)); view.write(value); } }
pub fn variableLengthStart(buf: *buffer.Buffer) !usize { try buf.write(&.{ 0, 0, 0, 0 }); // length placeholder return buf.len(); }
pub fn variableLengthFill(buf: *buffer.Buffer, pos: usize) void { const len = buf.len() - pos; var encoded_len: [4]u8 = undefined; std.mem.writeInt(i32, &encoded_len, @intCast(len), .big); buf.writeAt(&encoded_len, pos - 4); }
pub fn writeTextEncodedArray(values: []const []const u8, values_len: usize, buf: *buffer.Buffer, format_pos: usize, writeFn: *const fn ([]const u8, *buffer.Buffer) void) !void { buf.writeAt(&text_encoding, format_pos); if (values.len == 0) { // empty array, with length prefix return buf.write(&.{ 0, 0, 0, 2, '{', '}' }); }
// We're relying one our caller to give us an accurate values_len // The total value length will be: // 2 + values_len + values.len // {} delimiter + given to us + ',' delimiter between values const max_len = 2 + values_len + values.len; try buf.ensureUnusedCapacity(max_len);
// our max_len is just an estimate, we'll get the actual length and fill // it in later, for now, we skip the length var view = try buf.skip(4); const start = buf.len(); buf.writeByteAssumeCapacity('{'); for (values) |value| { writeFn(value, buf); buf.writeByteAssumeCapacity(','); }
// strip out last comma buf.truncate(1); buf.writeByteAssumeCapacity('}'); // -6 since the oid and the view.writeIntBig(i32, @intCast(buf.len() - start)); }
// Fairly special case for text-encoded arrays where we _always_ want to quote the value // but don't need to escape. This idea is taken from Java's PostgreSQL JDBC driver // specificallly for dealing with possible scientific notation in float/numeric text values pub fn writeTextEncodedEscapedArray(values: []const []const u8, buf: *buffer.Buffer, format_pos: usize) !void { var l: usize = 0; for (values) |v| { // +2 for the quotes around the value we'll need l += v.len + 2; } return Encode.writeTextEncodedArray(values, l, buf, format_pos, writeQuotedValue); }
fn writeQuotedValue(value: []const u8, buf: *buffer.Buffer) void { buf.writeByteAssumeCapacity('"'); buf.writeAssumeCapacity(value); buf.writeByteAssumeCapacity('"'); }
// Fairly special case for text-encoded arrays where we _always_ want to quote the value // but don't need to escape. This idea is taken from Java's PostgreSQL JDBC driver // specificallly for dealing with possible scientific notation in float/numeric text values pub fn writeTextEncodedRawArray(values: []const []const u8, buf: *buffer.Buffer, format_pos: usize) !void { var l: usize = 0; for (values) |v| { l += v.len; } return Encode.writeTextEncodedArray(values, l, buf, format_pos, writeRawValue); }
fn writeRawValue(value: []const u8, buf: *buffer.Buffer) void { buf.writeAssumeCapacity(value); }
pub fn writeTextEncodedCharArray(values: []const u8, buf: *buffer.Buffer, format_pos: usize) !void { buf.writeAt(&text_encoding, format_pos); if (values.len == 0) { // empty array, with length prefix return buf.write(&.{ 0, 0, 0, 2, '{', '}' }); }
// 6 = 4-byte length + opening brace + closing brace // v.len * 5 is the max guess about how much room we'll need. 1 byte // per character, delimiter + double quotes + escape const estimated_len: usize = 6 + values.len * 5; try buf.ensureUnusedCapacity(estimated_len);
// skip the length, which we'll fill later var view = try buf.skip(4); const start = buf.len();
// https://www.postgresql.org/docs/current/arrays.html#ARRAYS-IO buf.writeByteAssumeCapacity('{'); for (values) |c| { if (c == '"' or c == '\\') { buf.writeAssumeCapacity("\"\\"); buf.writeByteAssumeCapacity(c); buf.writeByteAssumeCapacity('"'); } else if (std.ascii.isWhitespace(c) or c == ',' or c == '{' or c == '}' or c == '\\') { buf.writeByteAssumeCapacity('"'); buf.writeByteAssumeCapacity(c); buf.writeByteAssumeCapacity('"'); } else { buf.writeByteAssumeCapacity(c); } buf.writeByteAssumeCapacity(','); }
// strip out last comma buf.truncate(1); buf.writeByteAssumeCapacity('}'); view.writeIntBig(i32, @intCast(buf.len() - start)); }};
pub fn oidToString(oid: i32) []const u8 { switch (oid) { 16 => return "T_bool", 17 => return "T_bytea", 18 => return "T_char", 19 => return "T_name", 20 => return "T_int8", 21 => return "T_int2", 22 => return "T_int2vector", 23 => return "T_int4", 24 => return "T_regproc", 25 => return "T_text", 26 => return "T_oid", 27 => return "T_tid", 28 => return "T_xid", 29 => return "T_cid", 30 => return "T_oidvector", 32 => return "T_pg_ddl_command", 71 => return "T_pg_type", 75 => return "T_pg_attribute", 81 => return "T_pg_proc", 83 => return "T_pg_class", 114 => return "T_json", 142 => return "T_xml", 143 => return "T__xml", 194 => return "T_pg_node_tree", 199 => return "T__json", 210 => return "T_smgr", 325 => return "T_index_am_handler", 600 => return "T_point", 601 => return "T_lseg", 602 => return "T_path", 603 => return "T_box", 604 => return "T_polygon", 628 => return "T_line", 629 => return "T__line", 650 => return "T_cidr", 651 => return "T__cidr", 700 => return "T_float4", 701 => return "T_float8", 702 => return "T_abstime", 703 => return "T_reltime", 704 => return "T_tinterval", 705 => return "T_unknown", 718 => return "T_circle", 719 => return "T__circle", 790 => return "T_money", 791 => return "T__money", 829 => return "T_macaddr", 869 => return "T_inet", 1000 => return "T__bool", 1001 => return "T__bytea", 1002 => return "T__char", 1003 => return "T__name", 1005 => return "T__int2", 1006 => return "T__int2vector", 1007 => return "T__int4", 1008 => return "T__regproc", 1009 => return "T__text", 1010 => return "T__tid", 1011 => return "T__xid", 1012 => return "T__cid", 1013 => return "T__oidvector", 1014 => return "T__bpchar", 1015 => return "T__varchar", 1016 => return "T__int8", 1017 => return "T__point", 1018 => return "T__lseg", 1019 => return "T__path", 1020 => return "T__box", 1021 => return "T__float4", 1022 => return "T__float8", 1023 => return "T__abstime", 1024 => return "T__reltime", 1025 => return "T__tinterval", 1027 => return "T__polygon", 1028 => return "T__oid", 1033 => return "T_aclitem", 1034 => return "T__aclitem", 1040 => return "T__macaddr", 1041 => return "T__inet", 1042 => return "T_bpchar", 1043 => return "T_varchar", 1082 => return "T_date", 1083 => return "T_time", 1114 => return "T_timestamp", 1115 => return "T__timestamp", 1182 => return "T__date", 1183 => return "T__time", 1184 => return "T_timestamptz", 1185 => return "T__timestamptz", 1186 => return "T_interval", 1187 => return "T__interval", 1231 => return "T__numeric", 1248 => return "T_pg_database", 1263 => return "T__cstring", 1266 => return "T_timetz", 1270 => return "T__timetz", 1560 => return "T_bit", 1561 => return "T__bit", 1562 => return "T_varbit", 1563 => return "T__varbit", 1700 => return "T_numeric", 1790 => return "T_refcursor", 2201 => return "T__refcursor", 2202 => return "T_regprocedure", 2203 => return "T_regoper", 2204 => return "T_regoperator", 2205 => return "T_regclass", 2206 => return "T_regtype", 2207 => return "T__regprocedure", 2208 => return "T__regoper", 2209 => return "T__regoperator", 2210 => return "T__regclass", 2211 => return "T__regtype", 2249 => return "T_record", 2275 => return "T_cstring", 2276 => return "T_any", 2277 => return "T_anyarray", 2278 => return "T_void", 2279 => return "T_trigger", 2280 => return "T_language_handler", 2281 => return "T_internal", 2282 => return "T_opaque", 2283 => return "T_anyelement", 2287 => return "T__record", 2776 => return "T_anynonarray", 2842 => return "T_pg_authid", 2843 => return "T_pg_auth_members", 2949 => return "T__txid_snapshot", 2950 => return "T_uuid", 2951 => return "T__uuid", 2970 => return "T_txid_snapshot", 3115 => return "T_fdw_handler", 3220 => return "T_pg_lsn", 3221 => return "T__pg_lsn", 3310 => return "T_tsm_handler", 3500 => return "T_anyenum", 3614 => return "T_tsvector", 3615 => return "T_tsquery", 3642 => return "T_gtsvector", 3643 => return "T__tsvector", 3644 => return "T__gtsvector", 3645 => return "T__tsquery", 3734 => return "T_regconfig", 3735 => return "T__regconfig", 3769 => return "T_regdictionary", 3770 => return "T__regdictionary", 3802 => return "T_jsonb", 3807 => return "T__jsonb", 3831 => return "T_anyrange", 3838 => return "T_event_trigger", 3904 => return "T_int4range", 3905 => return "T__int4range", 3906 => return "T_numrange", 3907 => return "T__numrange", 3908 => return "T_tsrange", 3909 => return "T__tsrange", 3910 => return "T_tstzrange", 3911 => return "T__tstzrange", 3912 => return "T_daterange", 3913 => return "T__daterange", 3926 => return "T_int8range", 3927 => return "T__int8range", 4066 => return "T_pg_shseclabel", 4089 => return "T_regnamespace", 4090 => return "T__regnamespace", 4096 => return "T_regrole", 4097 => return "T__regrole", else => return "unknown", }}
// The oid is what PG is expecting. In some cases, we'll use that to figure// out what to do.pub fn bindValue(comptime T: type, oid: i32, value: anytype, buf: *buffer.Buffer, format_pos: usize) !void { switch (@typeInfo(T)) { .null => { // type can stay 0 (text) // special length of -1 indicates null, no other data for this value return buf.write(&.{ 255, 255, 255, 255 }); }, .comptime_int => switch (oid) { Int16.oid.decimal => { if (value > 32767 or value < -32768) return error.IntWontFit; return Int16.encode(@intCast(value), buf, format_pos); }, Int32.oid.decimal => { if (value > 2147483647 or value < -2147483648) return error.IntWontFit; return Int32.encode(@intCast(value), buf, format_pos); }, Timestamp.oid.decimal, TimestampTz.oid.decimal => return Timestamp.encode(@intCast(value), buf, format_pos), Numeric.oid.decimal => return Numeric.encode(@as(f64, @floatFromInt(value)), buf, format_pos), Char.oid.decimal => { if (value > 255 or value < 0) return error.IntWontFit; return Char.encode(@intCast(value), buf, format_pos); }, Int64.oid.decimal, PgLSN.oid.decimal, Xid8.oid.decimal => return Int64.encode(@intCast(value), buf, format_pos), else => return error.BindWrongType, }, .int => switch (oid) { Int16.oid.decimal => { if (value > 32767 or value < -32768) return error.IntWontFit; return Int16.encode(@intCast(value), buf, format_pos); }, Int32.oid.decimal, Xid.oid.decimal => { if (value > 2147483647 or value < -2147483648) return error.IntWontFit; return Int32.encode(@intCast(value), buf, format_pos); }, Timestamp.oid.decimal, TimestampTz.oid.decimal => return Timestamp.encode(@intCast(value), buf, format_pos), Numeric.oid.decimal => return Numeric.encode(@as(f64, @floatFromInt(value)), buf, format_pos), Char.oid.decimal => { if (value > 255 or value < 0) return error.IntWontFit; return Char.encode(@intCast(value), buf, format_pos); }, Int64.oid.decimal, PgLSN.oid.decimal, Xid8.oid.decimal => { if (value > 9223372036854775807 or value < -9223372036854775808) { return error.IntWontFit; } return Int64.encode(@intCast(value), buf, format_pos); }, else => return error.BindWrongType, }, .comptime_float => switch (oid) { Float64.oid.decimal => return Float64.encode(@floatCast(value), buf, format_pos), Float32.oid.decimal => return Float32.encode(@floatCast(value), buf, format_pos), Numeric.oid.decimal => return Numeric.encode(value, buf, format_pos), else => return error.BindWrongType, }, .float => switch (oid) { Float64.oid.decimal => return Float64.encode(@floatCast(value), buf, format_pos), Float32.oid.decimal => return Float32.encode(@floatCast(value), buf, format_pos), Numeric.oid.decimal => return Numeric.encode(value, buf, format_pos), else => return error.BindWrongType, }, .bool => switch (oid) { Bool.oid.decimal => return Bool.encode(value, buf, format_pos), else => return error.BindWrongType, }, .pointer => |ptr| switch (ptr.size) { .slice => { if (ptr.is_const) { return bindSlice(oid, @as([]const ptr.child, value), buf, format_pos); } else { return bindSlice(oid, @as([]ptr.child, value), buf, format_pos); } }, .one => switch (@typeInfo(ptr.child)) { .array => { const Slice = []const std.meta.Elem(ptr.child); return bindSlice(oid, @as(Slice, value), buf, format_pos); }, .@"struct" => switch (oid) { JSON.oid.decimal => return JSON.encode(value, buf, format_pos), JSONB.oid.decimal => return JSONB.encode(value, buf, format_pos), else => return error.CannotBindStruct, }, else => compileHaltBindError(T), }, else => compileHaltBindError(T), }, .array => return bindValue(@TypeOf(&value), oid, &value, buf, format_pos), .@"struct" => return bindValue(@TypeOf(&value), oid, &value, buf, format_pos), .optional => |opt| { if (value) |v| { return bindValue(opt.child, oid, v, buf, format_pos); } // null return buf.write(&.{ 255, 255, 255, 255 }); }, .@"enum", .enum_literal => return String.encode(@tagName(value), buf, format_pos), else => compileHaltBindError(T), }}
fn bindSlice(oid: i32, value: anytype, buf: *buffer.Buffer, format_pos: usize) !void { const T = @TypeOf(value); if (T == []u8 or T == []const u8) { switch (oid) { Bytea.oid.decimal => return Bytea.encode(value, buf, format_pos), UUID.oid.decimal => return UUID.encode(value, buf, format_pos), JSONB.oid.decimal => return JSONB.encodeBytes(value, buf, format_pos), JSON.oid.decimal => return JSON.encodeBytes(value, buf, format_pos), MacAddr.oid.decimal => return MacAddr.encode(value, buf, format_pos), MacAddr8.oid.decimal => return MacAddr8.encode(value, buf, format_pos), CharArray.oid.decimal => { // This is actually an array, and in theory we could let it fallthrough // to the binary-array handling. BUT, if we do that, the code won't compile // because it would mean T can be []u8 or []const u8, and that makes parts // of the code invalid. Also, encoding a char array using the text protocol // is going to be more efficient than encoding it using the binary protocol. return Encode.writeTextEncodedCharArray(value, buf, format_pos); }, else => return String.encode(value, buf, format_pos), } }
// For now, a few types are text-encoded. This largely has to do with the fact // that there's no native Zig type, so a text representation lets us use PG's // own text->type conversion. if (comptime isStringArray(T)) { switch (oid) { TimestampArray.oid.decimal, NumericArray.oid.decimal => return Encode.writeTextEncodedEscapedArray(value, buf, format_pos), TimestampTzArray.oid.decimal, CidrArray.oid.decimal, CidrArray.inet_oid.decimal => return Encode.writeTextEncodedRawArray(value, buf, format_pos), MacAddrArray.oid.decimal => return MacAddrArray.encode(value, buf, format_pos), MacAddr8Array.oid.decimal => return MacAddr8Array.encode(value, buf, format_pos), else => {}, // fallthrough to binary encoding } }
// We have an array. All arrays have the same header. We'll write this into // buf now. It's possible we don't support the array type, so this can still // fail.
// arrays are always binary encoded (for now...)
buf.writeAt(&binary_encoding, format_pos);
const start_pos = buf.len();
try buf.write(&.{ 0, 0, 0, 0, // placeholder for the length of this parameter 0, 0, 0, 1, // number of dimensions, for now, we only support one 0, 0, 0, 0, // bitmask of null, currently, with a single dimension, we don't have null arrays 0, 0, 0, 0, // placeholder for the oid of each value });
// where in buf, to write the OID of the values const oid_pos = buf.len() - 4;
// number of values in our first (and currently only) dimension try buf.writeIntBig(i32, @intCast(value.len)); try buf.write(&.{ 0, 0, 0, 1 }); // lower bound of this demension
const ElemT = @typeInfo(T).pointer.child; switch (@typeInfo(ElemT)) { .int => |int| { if (int.signedness == .signed) { switch (int.bits) { 16 => try Int16Array.encode(value, buf, oid_pos), 32 => try Int32Array.encode(value, buf, oid_pos), 64 => { switch (oid) { TimestampArray.oid.decimal => try TimestampArray.encode(value, buf, oid_pos), TimestampTzArray.oid.decimal => try TimestampTzArray.encode(value, buf, oid_pos), else => try Int64Array.encode(value, buf, oid_pos), } }, else => compileHaltBindError(T), } } else { switch (int.bits) { 8 => try CharArray.encodeOne(value, buf, oid_pos), 16 => try Int16Array.encodeUnsigned(value, buf, oid_pos), 32 => try Int32Array.encodeUnsigned(value, buf, oid_pos), 64 => try Int64Array.encodeUnsigned(value, buf, oid_pos), else => compileHaltBindError(T), } } }, .float => |float| { if (oid == NumericArray.oid.decimal) { try NumericArray.encode(value, buf, oid_pos); } else switch (float.bits) { 32 => try Float32Array.encode(value, buf, oid_pos), 64 => try Float64Array.encode(value, buf, oid_pos), else => compileHaltBindError(T), } }, .bool => try BoolArray.encode(value, buf, oid_pos), .pointer => |ptr| switch (ptr.size) { .slice => switch (ptr.child) { u8 => switch (oid) { StringArray.oid.decimal => try StringArray.encode(value, buf, oid_pos), UUIDArray.oid.decimal => try UUIDArray.encode(value, buf, oid_pos), JSONBArray.oid.decimal => try JSONBArray.encode(value, buf, oid_pos), JSONArray.oid.decimal => try JSONArray.encode(value, buf, oid_pos), CharArray.oid.decimal => try CharArray.encode(value, buf, oid_pos), // we try this as a default to support user defined types with unknown oids // (like an array of enums) else => try ByteaArray.encode(value, buf, oid_pos), }, else => compileHaltBindError(T), }, else => compileHaltBindError(T), }, .@"enum", .enum_literal => try StringArray.encodeEnum(&value, buf, oid_pos), .array => try bindSlice(oid, &value, buf, format_pos), else => compileHaltBindError(T), }
var param_len: [4]u8 = undefined; // write the lenght of the parameter, -4 because for paremeters, the length // prefix itself isn't included. std.mem.writeInt(i32, ¶m_len, @intCast(buf.len() - start_pos - 4), .big); buf.writeAt(¶m_len, start_pos);}
fn isStringArray(comptime T: type) bool { switch (@typeInfo(T)) { .pointer => |ptr| switch (ptr.size) { .slice => switch (ptr.child) { []u8, []const u8 => return true, else => return false, }, else => return false, }, else => return false, }}
// Write the last part of the Bind message: telling postgresql how it should// encode each column of the responsepub fn resultEncoding(oids: []i32, buf: *buffer.Buffer) !void { if (oids.len == 0) { return buf.write(&.{ 0, 0 }); // we are specifying 0 return types }
// 2 bytes for the # of columns we're specifying + 2 bytes per column const space_needed = 2 + oids.len * 2; var view = try buf.skip(space_needed);
view.writeIntBig(u16, @intCast(oids.len)); for (oids) |oid| { view.write(resultEncodingFor(oid)); }}
fn compileHaltBindError(comptime T: type) noreturn { @compileError("cannot bind value of type " ++ @typeName(T));}
const t = lib.testing;test "UUID: toString" { try t.expectError(error.InvalidUUID, UUID.toString(&.{ 73, 190, 142, 9, 170, 250, 176, 16, 73, 21 }));
const s = try UUID.toString(&.{ 183, 204, 40, 47, 236, 67, 73, 190, 142, 9, 170, 250, 176, 16, 73, 21 }); try t.expectString("b7cc282f-ec43-49be-8e09-aafab0104915", &s);}
test "UUID: toBytes" { try t.expectError(error.InvalidUUID, UUID.toBytes(""));
{ const s = try UUID.toBytes("166B4751-D702-4FB9-9A2A-CD6B69ED18D6"); try t.expectSlice(u8, &.{ 22, 107, 71, 81, 215, 2, 79, 185, 154, 42, 205, 107, 105, 237, 24, 214 }, &s); }
{ const s = try UUID.toBytes("166b4751-d702-4fb9-9a2a-cd6b69ed18d7"); try t.expectSlice(u8, &.{ 22, 107, 71, 81, 215, 2, 79, 185, 154, 42, 205, 107, 105, 237, 24, 215 }, &s); }}