FlightGear next
sqlitelib.cxx
Go to the documentation of this file.
1#include "config.h"
2
3#include <cstdlib>
4#include <cstring>
5
6#include <string>
7
8#include <simgear/nasal/nasal.h>
9#include <simgear/misc/sg_path.hxx>
10#include <simgear/structure/exception.hxx>
11#include <simgear/debug/logstream.hxx>
12
13#ifdef SYSTEM_SQLITE
14 #include "sqlite3.h"
15#else
16 #include "fg_sqlite3.h"
17#endif
18
19// Ghost types
20struct DBGhost { sqlite3* db; };
21struct StmtGhost { sqlite3_stmt* stmt; };
22static void dbDestroy(struct DBGhost* g);
23static void stmtDestroy(struct StmtGhost* g);
24static naGhostType DBType = { (void(*)(void*))dbDestroy, "sqlite_db" };
25static naGhostType StmtType = { (void(*)(void*))stmtDestroy, "sqlite_statement" };
26#define DBG(r) ((naGhost_type(r) == &DBType) ? (struct DBGhost*)naGhost_ptr(r) : 0)
27#define STMTG(r) ((naGhost_type(r) == &StmtType) ? (struct StmtGhost*)naGhost_ptr(r) : 0)
28
29static void dbDestroy(struct DBGhost* g)
30{
31 if(g->db) sqlite3_close_v2(g->db);
32 g->db = 0;
33 free(g);
34}
35
36static void stmtDestroy(struct StmtGhost* g)
37{
38 if(g->stmt) sqlite3_finalize(g->stmt);
39 g->stmt = 0;
40 free(g);
41}
42
43static naRef f_open(naContext c, naRef me, int argc, naRef* args)
44{
45 struct DBGhost* g;
46 if(argc < 1 || !naIsString(args[0]))
47 naRuntimeError(c, "Bad/missing argument to sqlite.open");
48 g = (DBGhost*)malloc(sizeof(struct DBGhost));
49
50 const auto path = SGPath::fromUtf8(naStr_data(args[0]));
51 const SGPath filename = SGPath(path).validate(true);
52 if (filename.isNull()) {
53 SG_LOG(SG_NASAL, SG_ALERT, "stat(): reading '" <<
54 naStr_data(args[0]) << "' denied (unauthorized directory - authorization"
55 " no longer follows symlinks; to authorize reading additional "
56 "directories, pass them to --allow-nasal-read)");
57 naRuntimeError(c, "stat(): access denied (unauthorized directory)");
58 return naNil();
59 }
60
61 int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
62 std::string pathUtf8 = path.utf8Str();
63 if(sqlite3_open_v2(pathUtf8.c_str(), &g->db, openFlags, NULL))
64 {
65 const char* msg = sqlite3_errmsg(g->db);
66 sqlite3_close_v2(g->db);
67 free(g);
68 naRuntimeError(c, "sqlite open error: %s", msg);
69 }
70 sqlite3_busy_timeout(g->db, 60*60*1000); /* One hour */
71 return naNewGhost(c, &DBType, g);
72}
73
74static naRef f_close(naContext c, naRef me, int argc, naRef* args)
75{
76 struct DBGhost* g = argc > 0 ? DBG(args[0]) : 0;
77 if(!g) naRuntimeError(c, "bad/missing argument to sqlite.close");
78 sqlite3_close_v2(g->db);
79 g->db = 0;
80 return naNil();
81}
82
83static naRef f_prepare(naContext c, naRef me, int argc, naRef* args)
84{
85 naRef db = argc > 0 ? args[0] : naNil();
86 naRef s = argc > 1 ? args[1] : naNil();
87 const char* tail;
88 struct StmtGhost* g;
89 struct DBGhost* dbg = DBG(db);
90 if(!naIsString(s) || !dbg)
91 naRuntimeError(c, "bad/missing argument to sqlite.prepare");
92 g = (StmtGhost*)malloc(sizeof(struct StmtGhost));
93 if(sqlite3_prepare(dbg->db, naStr_data(s), naStr_len(s), &g->stmt, &tail))
94 {
95 const char* msg = sqlite3_errmsg(dbg->db);
96 if(g->stmt) sqlite3_finalize(g->stmt);
97 free(g);
98 naRuntimeError(c, "sqlite prepare error: %s", msg);
99 }
100 return naNewGhost(c, &StmtType, g);
101}
102
103// FIXME: need to catch and re-throw errors from the callback. Right
104// now they just get silently eaten.
105static naRef run_query(naContext c, sqlite3* db, sqlite3_stmt* stmt,
106 naRef callback)
107{
108 naContext subc = naIsNil(callback) ? 0 : naSubContext(c);
109 naRef* fields = 0;
110 naRef val, row, result = subc ? naNil() : naNewVector(c);
111 int i, cols=0, stat;
112 while((stat = sqlite3_step(stmt)) != SQLITE_DONE) {
113 if(stat != SQLITE_ROW)
114 naRuntimeError(c, "sqlite step error: %s", sqlite3_errmsg(db));
115 if(!fields) {
116 cols = sqlite3_column_count(stmt);
117 fields = (naRef*)malloc(cols * sizeof(naRef));
118 for(i=0; i<cols; i++) {
119 const char* s = sqlite3_column_name(stmt, i);
120 naRef fn = naStr_fromdata(naNewString(c), (char*)s, strlen(s));
121 // Do we really want to use the global intern table here?
122 fields[i] = naInternSymbol(fn);
123 }
124 }
125 row = naNewHash(c);
126 for(i=0; i<cols; i++) {
127 int type = sqlite3_column_type(stmt, i);
128 if(type == SQLITE_BLOB || type == SQLITE_TEXT)
129 val = naStr_fromdata(naNewString(c),
130 (char*)sqlite3_column_blob(stmt, i),
131 sqlite3_column_bytes(stmt, i));
132 else
133 val = naNum(sqlite3_column_double(stmt, i));
134 naHash_set(row, fields[i], val);
135 }
136 if(!subc) {
137 naVec_append(result, row);
138 } else {
139 naCall(subc, callback, 1, &row, naNil(), naNil());
140 if(naGetError(subc)) naRethrowError(subc);
141 }
142 }
143 if(subc) naFreeContext(subc);
144 free(fields);
145 return result;
146}
147
148static naRef f_exec(naContext c, naRef me, int argc, naRef* args)
149{
150 naRef callback = naNil();
151 naRef db = argc > 0 ? args[0] : naNil();
152 naRef stmt = argc > 1 ? args[1] : naNil();
153 int i, bindings = 2;
154 if(naIsString(stmt)) {
155 naRef args[2]; args[0] = db; args[1] = stmt;
156 stmt = f_prepare(c, naNil(), 2, args);
157 }
158 if(!DBG(db) || !STMTG(stmt))
159 naRuntimeError(c, "bad/missing argument to sqlite.exec");
160 if(argc > bindings && naIsFunc(args[bindings]))
161 callback = args[bindings++];
162 sqlite3_reset(STMTG(stmt)->stmt);
163 for(i=bindings; i<argc; i++) {
164 int err=0, bidx=i-bindings+1;
165 if(naIsString(args[i]))
166 err = sqlite3_bind_text(STMTG(stmt)->stmt, bidx,
167 naStr_data(args[i]),
168 naStr_len(args[i]), SQLITE_TRANSIENT);
169 else if(naIsNum(args[i]))
170 err = sqlite3_bind_double(STMTG(stmt)->stmt, bidx, args[i].num);
171 else
172 naRuntimeError(c, "sqlite.exec cannot bind non-scalar");
173 if(err)
174 naRuntimeError(c, "sqlite bind error: %s",
175 sqlite3_errmsg(DBG(db)->db));
176 }
177 return run_query(c, DBG(db)->db, STMTG(stmt)->stmt, callback);
178}
179
180static naRef f_finalize(naContext c, naRef me, int argc, naRef* args)
181{
182 struct StmtGhost* g = argc > 0 ? STMTG(args[0]) : 0;
183 if(!g) naRuntimeError(c, "bad/missing argument to sqlite.finalize");
184 sqlite3_finalize(g->stmt);
185 g->stmt = 0;
186 return naNil();
187}
188
189static naCFuncItem funcs[] = {
190 { (char*)"open", f_open },
191 { (char*)"close", f_close },
192 { (char*)"prepare", f_prepare },
193 { (char*)"exec", f_exec },
194 { (char*)"finalize", f_finalize },
195 { 0 }
196};
197
198naRef naInit_sqlite(naContext c)
199{
200 return naGenLib(c, funcs);
201}
static struct @032150236342374176343243244364035346052374146336 funcs[]
static naRef f_open(naContext c, naRef me, int argc, naRef *args)
Definition NasalSys.cxx:860
#define i(x)
static naGhostType StmtType
Definition sqlitelib.cxx:25
naRef naInit_sqlite(naContext c)
static naRef f_close(naContext c, naRef me, int argc, naRef *args)
Definition sqlitelib.cxx:74
static void stmtDestroy(struct StmtGhost *g)
Definition sqlitelib.cxx:36
static naRef f_exec(naContext c, naRef me, int argc, naRef *args)
static naRef f_open(naContext c, naRef me, int argc, naRef *args)
Definition sqlitelib.cxx:43
static void dbDestroy(struct DBGhost *g)
Definition sqlitelib.cxx:29
static naRef f_prepare(naContext c, naRef me, int argc, naRef *args)
Definition sqlitelib.cxx:83
static naGhostType DBType
Definition sqlitelib.cxx:24
static naRef run_query(naContext c, sqlite3 *db, sqlite3_stmt *stmt, naRef callback)
#define STMTG(r)
Definition sqlitelib.cxx:27
#define DBG(r)
Definition sqlitelib.cxx:26
static naRef f_finalize(naContext c, naRef me, int argc, naRef *args)
sqlite3 * db
Definition sqlitelib.cxx:20
sqlite3_stmt * stmt
Definition sqlitelib.cxx:21