prefect server in zig
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const backend = @import("backend.zig");
5const log = @import("../logging.zig");
6
7pub const BlockDocumentRow = struct {
8 id: []const u8,
9 created: []const u8,
10 updated: []const u8,
11 name: ?[]const u8,
12 data: []const u8,
13 is_anonymous: bool,
14 block_type_id: []const u8,
15 block_type_name: ?[]const u8,
16 block_schema_id: []const u8,
17};
18
19pub fn insert(
20 id: []const u8,
21 name: ?[]const u8,
22 data: []const u8,
23 is_anonymous: bool,
24 block_type_id: []const u8,
25 block_type_name: ?[]const u8,
26 block_schema_id: []const u8,
27) !void {
28 backend.db.exec(
29 \\INSERT INTO block_document (id, name, data, is_anonymous, block_type_id, block_type_name, block_schema_id)
30 \\VALUES (?, ?, ?, ?, ?, ?, ?)
31 , .{
32 id,
33 name,
34 data,
35 @as(i32, if (is_anonymous) 1 else 0),
36 block_type_id,
37 block_type_name,
38 block_schema_id,
39 }) catch |err| {
40 log.err("database", "insert block_document error: {}", .{err});
41 return err;
42 };
43}
44
45pub fn getById(alloc: Allocator, id: []const u8) !?BlockDocumentRow {
46 var rows = backend.db.query(
47 \\SELECT id, created, updated, name, data, is_anonymous,
48 \\ block_type_id, block_type_name, block_schema_id
49 \\FROM block_document WHERE id = ?
50 , .{id}) catch return null;
51 defer rows.deinit();
52
53 if (rows.next()) |row| {
54 return rowToBlockDocument(alloc, row);
55 }
56 return null;
57}
58
59pub fn getByTypeSlugAndName(
60 alloc: Allocator,
61 block_type_slug: []const u8,
62 name: []const u8,
63) !?BlockDocumentRow {
64 var rows = backend.db.query(
65 \\SELECT bd.id, bd.created, bd.updated, bd.name, bd.data, bd.is_anonymous,
66 \\ bd.block_type_id, bd.block_type_name, bd.block_schema_id
67 \\FROM block_document bd
68 \\JOIN block_type bt ON bd.block_type_id = bt.id
69 \\WHERE bt.slug = ? AND bd.name = ?
70 , .{ block_type_slug, name }) catch return null;
71 defer rows.deinit();
72
73 if (rows.next()) |row| {
74 return rowToBlockDocument(alloc, row);
75 }
76 return null;
77}
78
79/// SQL for update - dialect-specific for datetime function
80const update_sql = struct {
81 const sqlite_with_schema = "UPDATE block_document SET data = ?, block_schema_id = ?, updated = datetime('now') WHERE id = ?";
82 const sqlite_no_schema = "UPDATE block_document SET data = ?, updated = datetime('now') WHERE id = ?";
83 const postgres_with_schema = "UPDATE block_document SET data = ?, block_schema_id = ?, updated = NOW()::TEXT WHERE id = ?";
84 const postgres_no_schema = "UPDATE block_document SET data = ?, updated = NOW()::TEXT WHERE id = ?";
85};
86
87pub fn update(
88 id: []const u8,
89 data: []const u8,
90 block_schema_id: ?[]const u8,
91) !void {
92 if (block_schema_id) |schema_id| {
93 const sql = switch (backend.db.dialect) {
94 .sqlite => update_sql.sqlite_with_schema,
95 .postgres => update_sql.postgres_with_schema,
96 };
97 backend.db.exec(sql, .{ data, schema_id, id }) catch |err| {
98 log.err("database", "update block_document error: {}", .{err});
99 return err;
100 };
101 } else {
102 const sql = switch (backend.db.dialect) {
103 .sqlite => update_sql.sqlite_no_schema,
104 .postgres => update_sql.postgres_no_schema,
105 };
106 backend.db.exec(sql, .{ data, id }) catch |err| {
107 log.err("database", "update block_document error: {}", .{err});
108 return err;
109 };
110 }
111}
112
113pub fn list(alloc: Allocator, limit: usize) ![]BlockDocumentRow {
114 var results = std.ArrayListUnmanaged(BlockDocumentRow){};
115 errdefer results.deinit(alloc);
116
117 var rows = backend.db.query(
118 \\SELECT id, created, updated, name, data, is_anonymous,
119 \\ block_type_id, block_type_name, block_schema_id
120 \\FROM block_document ORDER BY created DESC LIMIT ?
121 , .{@as(i64, @intCast(limit))}) catch |err| {
122 log.err("database", "list block_documents error: {}", .{err});
123 return err;
124 };
125 defer rows.deinit();
126
127 while (rows.next()) |row| {
128 try results.append(alloc, rowToBlockDocument(alloc, row));
129 }
130
131 return results.toOwnedSlice(alloc);
132}
133
134pub fn delete(id: []const u8) !bool {
135 const affected = backend.db.execWithRowCount(
136 "DELETE FROM block_document WHERE id = ?",
137 .{id},
138 ) catch |err| {
139 log.err("database", "delete block_document error: {}", .{err});
140 return err;
141 };
142 return affected > 0;
143}
144
145pub fn listByTypeSlug(alloc: Allocator, block_type_slug: []const u8, limit: usize) ![]BlockDocumentRow {
146 var results = std.ArrayListUnmanaged(BlockDocumentRow){};
147 errdefer results.deinit(alloc);
148
149 var rows = backend.db.query(
150 \\SELECT bd.id, bd.created, bd.updated, bd.name, bd.data, bd.is_anonymous,
151 \\ bd.block_type_id, bd.block_type_name, bd.block_schema_id
152 \\FROM block_document bd
153 \\JOIN block_type bt ON bd.block_type_id = bt.id
154 \\WHERE bt.slug = ?
155 \\ORDER BY bd.created DESC LIMIT ?
156 , .{ block_type_slug, @as(i64, @intCast(limit)) }) catch |err| {
157 log.err("database", "list block_documents by type error: {}", .{err});
158 return err;
159 };
160 defer rows.deinit();
161
162 while (rows.next()) |row| {
163 try results.append(alloc, rowToBlockDocument(alloc, row));
164 }
165
166 return results.toOwnedSlice(alloc);
167}
168
169fn rowToBlockDocument(alloc: Allocator, row: anytype) BlockDocumentRow {
170 return .{
171 .id = alloc.dupe(u8, row.text(0)) catch "",
172 .created = alloc.dupe(u8, row.text(1)) catch "",
173 .updated = alloc.dupe(u8, row.text(2)) catch "",
174 .name = if (row.text(3).len > 0) alloc.dupe(u8, row.text(3)) catch null else null,
175 .data = alloc.dupe(u8, row.text(4)) catch "{}",
176 .is_anonymous = row.int(5) != 0,
177 .block_type_id = alloc.dupe(u8, row.text(6)) catch "",
178 .block_type_name = if (row.text(7).len > 0) alloc.dupe(u8, row.text(7)) catch null else null,
179 .block_schema_id = alloc.dupe(u8, row.text(8)) catch "",
180 };
181}