const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; pub const Dialect = enum { sqlite, postgres, /// Returns the function/expression for current timestamp pub fn now(self: Dialect) []const u8 { return switch (self) { .sqlite => "datetime('now')", .postgres => "NOW()", }; } /// Returns the placeholder format for parameter N (1-indexed) pub fn placeholder(self: Dialect, buf: *[8]u8, n: usize) []const u8 { return switch (self) { .sqlite => "?", .postgres => std.fmt.bufPrint(buf, "${d}", .{n}) catch "?", }; } /// Rewrites SQL with ? placeholders to dialect-specific format /// For SQLite: returns input unchanged /// For PostgreSQL: rewrites ? to $1, $2, etc. pub fn rewritePlaceholders(self: Dialect, alloc: Allocator, sql: []const u8) ![]const u8 { if (self == .sqlite) { return sql; // no rewrite needed } // count placeholders var count: usize = 0; for (sql) |c| { if (c == '?') count += 1; } if (count == 0) { return sql; } // allocate output (each ? becomes $N, max 4 chars for reasonable N) var output = try alloc.alloc(u8, sql.len + count * 4); var out_idx: usize = 0; var param_num: usize = 1; for (sql) |c| { if (c == '?') { var buf: [8]u8 = undefined; const ph = std.fmt.bufPrint(&buf, "${d}", .{param_num}) catch "$?"; @memcpy(output[out_idx..][0..ph.len], ph); out_idx += ph.len; param_num += 1; } else { output[out_idx] = c; out_idx += 1; } } // Shrink to exact size so free() works correctly if (alloc.resize(output, out_idx)) { return output[0..out_idx]; } // If resize not supported, realloc const exact = alloc.realloc(output, out_idx) catch return output[0..out_idx]; return exact; } /// Returns the INSERT conflict ignore syntax /// SQLite: INSERT OR IGNORE INTO ... /// PostgreSQL: INSERT INTO ... ON CONFLICT DO NOTHING pub fn insertOrIgnorePrefix(self: Dialect) []const u8 { return switch (self) { .sqlite => "INSERT OR IGNORE INTO", .postgres => "INSERT INTO", }; } pub fn insertOrIgnoreSuffix(self: Dialect) []const u8 { return switch (self) { .sqlite => "", .postgres => " ON CONFLICT DO NOTHING", }; } }; test "rewritePlaceholders - sqlite unchanged" { const alloc = std.testing.allocator; const sql = "SELECT * FROM foo WHERE a = ? AND b = ?"; const result = try Dialect.sqlite.rewritePlaceholders(alloc, sql); try std.testing.expectEqualStrings(sql, result); } test "rewritePlaceholders - postgres" { const alloc = std.testing.allocator; const sql = "SELECT * FROM foo WHERE a = ? AND b = ?"; const result = try Dialect.postgres.rewritePlaceholders(alloc, sql); defer alloc.free(result); try std.testing.expectEqualStrings("SELECT * FROM foo WHERE a = $1 AND b = $2", result); }