Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4ec0d17

Browse files
committed
Merge pull request #96 from robotii/sqlite
Add sqlite library bindings
2 parents 3c606f8 + 3a3c6a7 commit 4ec0d17

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed

‎lib/database.c‎

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#include "potion.h"
2+
#include <sqlite3.h>
3+
#include <stdio.h>
4+
5+
#define PN_GET_DATABASE(t) ((struct PNDatabase *)potion_fwd((PN)t))
6+
PNType database_type;
7+
8+
struct PNDatabase {
9+
PN_OBJECT_HEADER;
10+
sqlite3 *db;
11+
};
12+
13+
struct PNCallback {
14+
PN_OBJECT_HEADER;
15+
PN_SIZE siz;
16+
Potion *P;
17+
PN cb;
18+
};
19+
20+
const int CallbackSize = sizeof(struct PNCallback) - sizeof(struct PNData);
21+
22+
PN potion_sqlite_open(Potion *P, PN cl, PN self, PN path);
23+
PN potion_callback(Potion *P, PN closure);
24+
25+
static int potion_database_callback(void *callback, int argc, char **argv, char **azColName) {
26+
struct PNCallback * cbp = (struct PNCallback *)callback;
27+
28+
if(cbp != NULL) {
29+
vPN(Closure) cb = PN_IS_CLOSURE(cbp->cb) ? PN_CLOSURE(cbp->cb) : NULL;
30+
31+
if(cb) {
32+
Potion *P = cbp->P;
33+
PN table = potion_table_empty(P);
34+
int i;
35+
for(i = 0; i < argc; i++){
36+
potion_table_put(P, PN_NIL, table, PN_STR(azColName[i]), argv[i] ? PN_STR(argv[i]) : PN_NIL);
37+
}
38+
39+
// Now call the callback with the table
40+
cb->method(P, (PN)cb, (PN)cb, (PN)table);
41+
}
42+
}
43+
return 0;
44+
}
45+
46+
PN potion_sqlite_new(Potion *P, PN cl, PN ign, PN path) {
47+
struct PNDatabase *self = (struct PNDatabase *)potion_gc_alloc(P, database_type, sizeof(struct PNDatabase));
48+
self->db = NULL;
49+
return potion_sqlite_open(P, cl, (PN)self, path);
50+
}
51+
52+
PN potion_sqlite_exec(Potion *P, PN cl, PN self, PN query, PN callback) {
53+
//TODO: Proper error checking
54+
if(!PN_IS_STR(query)) {
55+
return PN_NIL;
56+
}
57+
58+
char *zErrMsg = 0;
59+
int rc;
60+
61+
struct PNDatabase *db = (struct PNDatabase *)self;
62+
struct PNCallback *cb = (struct PNCallback*)potion_callback(P, callback);
63+
64+
if(db->db == NULL) {
65+
//FIXME: Return a proper error
66+
return potion_io_error(P, "exec");
67+
}
68+
69+
rc = sqlite3_exec(db->db, PN_STR_PTR(query), potion_database_callback, cb, &zErrMsg);
70+
71+
if(rc != SQLITE_OK) {
72+
// Convert to potion string and return it as an error
73+
fprintf(stderr, "SQL error: %s\n", zErrMsg);
74+
PN e = potion_io_error(P, zErrMsg);
75+
sqlite3_free(zErrMsg);
76+
return e;
77+
}
78+
return self;
79+
}
80+
81+
PN potion_sqlite_gettable(Potion *P, PN cl, PN self, PN query) {
82+
//TODO: Proper error checking
83+
if(!PN_IS_STR(query)) {
84+
return PN_NIL;
85+
}
86+
87+
char *q = PN_STR_PTR(query);
88+
struct PNDatabase *database = PN_GET_DATABASE(self);
89+
90+
if(!database->db) {
91+
return PN_NIL;
92+
}
93+
94+
char **pazResult; /* Results of the query */
95+
int pnRow; /* Number of result rows written here */
96+
int pnColumn; /* Number of result columns written here */
97+
char *pzErrmsg; /* Error msg written here */
98+
int rc = sqlite3_get_table(database->db, q, &pazResult, &pnRow, &pnColumn, &pzErrmsg);
99+
100+
// Check for error
101+
if(rc != SQLITE_OK) {
102+
// Convert to potion string and return it as an error
103+
fprintf(stderr, "SQL error: %s\n", pzErrmsg);
104+
PN e = potion_io_error(P, pzErrmsg);
105+
sqlite3_free(pzErrmsg);
106+
return e;
107+
}
108+
109+
// Process the table
110+
int i, j;
111+
PN tuple = potion_tuple_empty(P);
112+
113+
// Loop over each row
114+
for(i = 1; i <= pnRow; i++) {
115+
PN table = potion_table_empty(P);
116+
for(j = 0; j < pnColumn; j++) {
117+
char *value = pazResult[i*pnColumn + j];
118+
potion_table_put(P, PN_NIL, table, PN_STR(pazResult[j]), value ? PN_STR(value) : PN_NIL);
119+
}
120+
PN_PUSH(tuple, table);
121+
}
122+
123+
sqlite3_free_table(pazResult);
124+
return tuple;
125+
}
126+
127+
PN potion_sqlite_close(Potion *P, PN cl, PN self) {
128+
struct PNDatabase *db = (struct PNDatabase *)self;
129+
if(db->db) {
130+
sqlite3_close(db->db);
131+
db->db = NULL;
132+
}
133+
return self;
134+
}
135+
136+
PN potion_sqlite_open(Potion *P, PN cl, PN self, PN path) {
137+
struct PNDatabase *database = (struct PNDatabase *)self;
138+
139+
//TODO: Proper error checking
140+
if(!PN_IS_STR(path)) {
141+
return PN_NIL;
142+
}
143+
144+
if(database->db) {
145+
potion_sqlite_close(P, cl, self);
146+
}
147+
148+
sqlite3 *db;
149+
int rc = sqlite3_open(PN_STR_PTR(path), &db);
150+
151+
if (rc)
152+
return potion_io_error(P, "open");
153+
154+
database->db = db;
155+
return self;
156+
}
157+
158+
PN potion_sqlite_isopen(Potion *P, PN cl, PN self) {
159+
struct PNDatabase *db = (struct PNDatabase *)self;
160+
return db->db ? PN_TRUE : PN_FALSE;
161+
}
162+
163+
PN potion_callback(Potion *P, PN closure) {
164+
struct PNCallback *cb = (struct PNCallback *)potion_data_alloc(P, CallbackSize);
165+
cb->siz = CallbackSize; // To help out GC
166+
cb->P = P;
167+
cb->cb = closure;
168+
return (PN)cb;
169+
}
170+
171+
void Potion_Init_database(Potion *P) {
172+
PN database_vt = potion_class(P, 0, 0, 0);
173+
database_type = potion_class_type(P, database_vt);
174+
potion_define_global(P, PN_STR("Database"), database_vt);
175+
176+
potion_type_constructor_is(database_vt, PN_FUNC(potion_sqlite_new, "path=S"));
177+
potion_method(database_vt, "exec", potion_sqlite_exec, "query=S|callback=o");
178+
potion_method(database_vt, "gettable", potion_sqlite_gettable, "query=S");
179+
potion_method(database_vt, "close", potion_sqlite_close, 0);
180+
potion_method(database_vt, "open", potion_sqlite_open, "path=S");
181+
potion_method(database_vt, "open?", potion_sqlite_isopen, 0);
182+
}
183+
184+
/*
185+
load "database"
186+
187+
db = Database("database.db")
188+
db exec "CREATE TABLE t (id INT, name TEXT)"
189+
db exec "INSERT INTO t (id,name) VALUES (1,'New name')"
190+
db close
191+
db open? say
192+
db open("database.db")
193+
db open? say
194+
db exec "INSERT INTO t (id,name) VALUES (2,'Old name')"
195+
db exec "SELECT * FROM t" (row): row say.
196+
db gettable "SELECT * FROM t" say
197+
db open? say
198+
db close
199+
200+
*/

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /