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
137
138
139
140
141
142
143
144
145
146
147
148
149
|
const z = @import( "std" );
const u = @import( "util.zig" );
const sql = @import( "sqlite" );
const alloc = u.alloc;
const ArenaAllocator = z.heap.ArenaAllocator;
const Mutex = z.Thread.Mutex;
const Db = sql.Db;
pub threadlocal var dbi: Interface = undefined;
pub threadlocal var dbi_init: bool = false;
var db_mutex = Mutex{};
pub fn create( dbfile: [:0]const u8 ) !void {
dbiCheck() catch try init( dbfile );
const create_users_q =
\\CREATE TABLE IF NOT EXISTS users (
\\ uuid TEXT PRIMARY KEY,
\\ email TEXT,
\\ tokens TEXT,
\\ login_token TEXT,
\\ token_resetdate INTEGER,
\\ api_tokens TEXT,
\\ created_at INTEGER,
\\ subscription_data TEXT
\\)
;
try dbi.put( create_users_q, .{} );
// ensure all fields actually exist in the database
dbi.put( "ALTER TABLE users ADD COLUMN email TEXT", .{} ) catch {};
dbi.put( "ALTER TABLE users ADD COLUMN tokens TEXT", .{} ) catch {};
dbi.put( "ALTER TABLE users ADD COLUMN login_token TEXT", .{} ) catch {};
dbi.put( "ALTER TABLE users ADD COLUMN token_resetdate INTEGER", .{} ) catch {};
dbi.put( "ALTER TABLE users ADD COLUMN api_tokens TEXT", .{} ) catch {};
dbi.put( "ALTER TABLE users ADD COLUMN created_at INTEGER", .{} ) catch {};
dbi.put( "ALTER TABLE users ADD COLUMN subscription_data TEXT", .{} ) catch {};
const create_userdata_q =
\\CREATE TABLE IF NOT EXISTS user_data (
\\ uuid TEXT PRIMARY KEY,
\\ nickname TEXT,
\\ prompt_data TEXT,
\\ site_prefs TEXT,
\\ chat_files TEXT
\\)
;
try dbi.put( create_userdata_q, .{} );
// ensure all fields actually exist in the database
dbi.put( "ALTER TABLE user_data ADD COLUMN nickname INTEGER", .{} ) catch {};
dbi.put( "ALTER TABLE user_data ADD COLUMN prompt_data INTEGER", .{} ) catch {};
dbi.put( "ALTER TABLE user_data ADD COLUMN site_prefs INTEGER", .{} ) catch {};
dbi.put( "ALTER TABLE user_data ADD COLUMN chat_files INTEGER", .{} ) catch {};
}
pub fn init( dbfile: [:0]const u8 ) !void {
if( dbi_init ) return error.DbiAlreadyInitialized;
const db = try sql.Db.init( .{
.mode = Db.Mode{ .File = dbfile },
.open_flags = .{
.write = true,
.create = true
},
.threading_mode = .MultiThread
} );
dbi = .{ .db = db };
dbi_init = true;
}
fn dbiCheck() !void {
if( !dbi_init ) return error.DbiNotInitialized;
}
///methods of t get ignored
pub fn Row( comptime t: type ) type {
return struct {
v: t,
alloc: ArenaAllocator,
pub fn deinit( self: @This() ) void {
self.alloc.deinit();
}
};
}
pub const Interface = struct {
const Self = Interface;
db: Db,
pub fn deinit( self: *Interface ) void {
self.db.deinit();
}
pub fn put( self: *Interface, comptime query: []const u8, value: anytype ) !void {
try dbiCheck();
db_mutex.lock();
defer db_mutex.unlock();
var statement = try self.db.prepare( query );
defer statement.deinit();
try statement.exec( .{}, value );
}
///returns the first row of the query
pub fn one( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) !t {
try dbiCheck();
db_mutex.lock();
defer db_mutex.unlock();
var statement = try self.db.prepare( query );
defer statement.deinit();
const ret = try statement.one( t, .{}, value );
return ret.?;
}
///returns all rows of the query
///must be freed by caller
pub fn all( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) ![]t {
try dbiCheck();
db_mutex.lock();
defer db_mutex.unlock();
var statement = try self.db.prepare( query );
defer statement.deinit();
return try statement.all( t, alloc, .{}, value );
}
pub fn oneAlloc( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) !Row(t) {
try dbiCheck();
db_mutex.lock();
defer db_mutex.unlock();
var arena = ArenaAllocator.init( alloc );
const a = arena.allocator();
var statement = try self.db.prepareDynamic( query );
defer statement.deinit();
const ret = try statement.oneAlloc( t, a, .{}, value );
if( ret == null ) return error.QueryError;
return .{ .v = ret.?, .alloc = arena };
}
};
|