prefect server in zig
1const std = @import("std");
2const mem = std.mem;
3const Allocator = mem.Allocator;
4
5pub const Dialect = enum {
6 sqlite,
7 postgres,
8
9 /// Returns the function/expression for current timestamp
10 pub fn now(self: Dialect) []const u8 {
11 return switch (self) {
12 .sqlite => "datetime('now')",
13 .postgres => "NOW()",
14 };
15 }
16
17 /// Returns the placeholder format for parameter N (1-indexed)
18 pub fn placeholder(self: Dialect, buf: *[8]u8, n: usize) []const u8 {
19 return switch (self) {
20 .sqlite => "?",
21 .postgres => std.fmt.bufPrint(buf, "${d}", .{n}) catch "?",
22 };
23 }
24
25 /// Rewrites SQL with ? placeholders to dialect-specific format
26 /// For SQLite: returns input unchanged
27 /// For PostgreSQL: rewrites ? to $1, $2, etc.
28 pub fn rewritePlaceholders(self: Dialect, alloc: Allocator, sql: []const u8) ![]const u8 {
29 if (self == .sqlite) {
30 return sql; // no rewrite needed
31 }
32
33 // count placeholders
34 var count: usize = 0;
35 for (sql) |c| {
36 if (c == '?') count += 1;
37 }
38
39 if (count == 0) {
40 return sql;
41 }
42
43 // allocate output (each ? becomes $N, max 4 chars for reasonable N)
44 var output = try alloc.alloc(u8, sql.len + count * 4);
45 var out_idx: usize = 0;
46 var param_num: usize = 1;
47
48 for (sql) |c| {
49 if (c == '?') {
50 var buf: [8]u8 = undefined;
51 const ph = std.fmt.bufPrint(&buf, "${d}", .{param_num}) catch "$?";
52 @memcpy(output[out_idx..][0..ph.len], ph);
53 out_idx += ph.len;
54 param_num += 1;
55 } else {
56 output[out_idx] = c;
57 out_idx += 1;
58 }
59 }
60
61 // Shrink to exact size so free() works correctly
62 if (alloc.resize(output, out_idx)) {
63 return output[0..out_idx];
64 }
65 // If resize not supported, realloc
66 const exact = alloc.realloc(output, out_idx) catch return output[0..out_idx];
67 return exact;
68 }
69
70 /// Returns the INSERT conflict ignore syntax
71 /// SQLite: INSERT OR IGNORE INTO ...
72 /// PostgreSQL: INSERT INTO ... ON CONFLICT DO NOTHING
73 pub fn insertOrIgnorePrefix(self: Dialect) []const u8 {
74 return switch (self) {
75 .sqlite => "INSERT OR IGNORE INTO",
76 .postgres => "INSERT INTO",
77 };
78 }
79
80 pub fn insertOrIgnoreSuffix(self: Dialect) []const u8 {
81 return switch (self) {
82 .sqlite => "",
83 .postgres => " ON CONFLICT DO NOTHING",
84 };
85 }
86};
87
88test "rewritePlaceholders - sqlite unchanged" {
89 const alloc = std.testing.allocator;
90 const sql = "SELECT * FROM foo WHERE a = ? AND b = ?";
91 const result = try Dialect.sqlite.rewritePlaceholders(alloc, sql);
92 try std.testing.expectEqualStrings(sql, result);
93}
94
95test "rewritePlaceholders - postgres" {
96 const alloc = std.testing.allocator;
97 const sql = "SELECT * FROM foo WHERE a = ? AND b = ?";
98 const result = try Dialect.postgres.rewritePlaceholders(alloc, sql);
99 defer alloc.free(result);
100 try std.testing.expectEqualStrings("SELECT * FROM foo WHERE a = $1 AND b = $2", result);
101}