1 /**
2 * libdbi database backend
3 *
4 * Part of Gammu project
5 *
6 * Copyright (c) 2009 - 2018 Michal Cihar <michal@cihar.com>
7 *
8 * Licensed under GNU GPL version 2 or later
9 */
10
11 #include <gammu.h>
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <errno.h>
17 #include <time.h>
18 #include <assert.h>
19 #ifdef WIN32
20 #include <windows.h>
21 #ifndef __GNUC__
22 #pragma comment(lib, "libdbi.lib")
23 #endif
24 #endif
25
26 #include "../core.h"
27 #include "sql.h"
28
SMSDDBI_GetNumber(GSM_SMSDConfig * Config,SQL_result * res,unsigned int field)29 long long SMSDDBI_GetNumber(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field)
30 {
31 unsigned int type;
32
33 field++;
34 type = dbi_result_get_field_type_idx(res->dbi, field);
35
36 switch (type) {
37 case DBI_TYPE_INTEGER:
38 type = dbi_result_get_field_attribs_idx(res->dbi, field);
39 if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE1) {
40 return dbi_result_get_int_idx(res->dbi, field);
41 } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE2) {
42 return dbi_result_get_int_idx(res->dbi, field);
43 } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE3) {
44 return dbi_result_get_int_idx(res->dbi, field);
45 } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE4) {
46 return dbi_result_get_int_idx(res->dbi, field);
47 } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE8) {
48 return dbi_result_get_longlong_idx(res->dbi, field);
49 }
50 SMSD_Log(DEBUG_ERROR, Config, "Wrong integer field subtype from DBI: %d", type);
51 return -1;
52 case DBI_TYPE_DECIMAL:
53 type = dbi_result_get_field_attribs_idx(res->dbi, field);
54 if ((type & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE4) {
55 return dbi_result_get_int_idx(res->dbi, field);
56 } else if ((type & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE8) {
57 return dbi_result_get_longlong_idx(res->dbi, field);
58 }
59 SMSD_Log(DEBUG_ERROR, Config, "Wrong decimal field subtype from DBI: %d", type);
60 return -1;
61 #ifdef DBI_TYPE_XDECIMAL
62 case DBI_TYPE_XDECIMAL:
63 return dbi_result_get_as_longlong_idx(res->dbi, field);
64 #endif
65 default:
66 SMSD_Log(DEBUG_ERROR, Config, "Wrong field type for number (not INTEGER nor DECIMAL) from DBI: %d", type);
67 return -1;
68 }
69 }
70
SMSDDBI_GetDate(GSM_SMSDConfig * Config,SQL_result * res,unsigned int field)71 time_t SMSDDBI_GetDate(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field)
72 {
73 unsigned int type;
74 const char *date;
75
76 field++;
77 type = dbi_result_get_field_type_idx(res->dbi, field);
78
79 switch (type) {
80 case DBI_TYPE_INTEGER:
81 case DBI_TYPE_DECIMAL:
82 #ifdef DBI_TYPE_XDECIMAL
83 case DBI_TYPE_XDECIMAL:
84 #endif
85 return SMSDDBI_GetNumber(Config, res, field);
86 case DBI_TYPE_STRING:
87 date = dbi_result_get_string_idx(res->dbi, field);
88 return SMSDSQL_ParseDate(Config, date);
89 case DBI_TYPE_DATETIME:
90 return dbi_result_get_datetime_idx(res->dbi, field);
91 case DBI_TYPE_ERROR:
92 default:
93 SMSD_Log(DEBUG_ERROR, Config, "Wrong field type for date from DBI: %d", type);
94 return -1;
95 }
96 }
97
SMSDDBI_GetBool(GSM_SMSDConfig * Config,SQL_result * res,unsigned int field)98 gboolean SMSDDBI_GetBool(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field)
99 {
100 unsigned int type;
101 const char *value;
102 int num;
103
104 field++;
105 type = dbi_result_get_field_type_idx(res->dbi, field);
106
107 switch (type) {
108 case DBI_TYPE_INTEGER:
109 case DBI_TYPE_DECIMAL:
110 #ifdef DBI_TYPE_XDECIMAL
111 case DBI_TYPE_XDECIMAL:
112 #endif
113 num = SMSDDBI_GetNumber(Config, res, field);
114 if (num == -1) {
115 return -1;
116 } else if (num == 0) {
117 return FALSE;
118 } else {
119 return TRUE;
120 }
121 case DBI_TYPE_STRING:
122 value = dbi_result_get_string_idx(res->dbi, field);
123 return GSM_StringToBool(value);
124 case DBI_TYPE_ERROR:
125 default:
126 SMSD_Log(DEBUG_ERROR, Config, "Wrong field type for boolean from DBI: %d", type);
127 return -1;
128 }
129 }
130
SMSDDBI_GetString(GSM_SMSDConfig * Config,SQL_result * res,unsigned int col)131 const char *SMSDDBI_GetString(GSM_SMSDConfig * Config, SQL_result *res, unsigned int col)
132 {
133 return dbi_result_get_string_idx(res->dbi, col+1);
134 }
135
SMSDDBI_LogError(GSM_SMSDConfig * Config)136 static void SMSDDBI_LogError(GSM_SMSDConfig * Config)
137 {
138 int rc;
139 const char *msg;
140 rc = dbi_conn_error(Config->conn.dbi, &msg);
141 if (rc == -1) {
142 SMSD_Log(DEBUG_ERROR, Config, "Unknown DBI error!");
143 } else {
144 SMSD_Log(DEBUG_ERROR, Config, "DBI error %d: %s", rc, msg);
145 }
146 }
147
SMSDDBI_Callback(dbi_conn Conn,void * Config)148 void SMSDDBI_Callback(dbi_conn Conn, void *Config)
149 {
150 SMSDDBI_LogError((GSM_SMSDConfig *) Config);
151 }
152
153 /* Disconnects from a database */
SMSDDBI_Free(GSM_SMSDConfig * Config)154 void SMSDDBI_Free(GSM_SMSDConfig * Config)
155 {
156 if (Config->conn.dbi != NULL) {
157 dbi_conn_close(Config->conn.dbi);
158 dbi_shutdown();
159 Config->conn.dbi = NULL;
160 }
161 }
162
163 /* Connects to database */
SMSDDBI_Connect(GSM_SMSDConfig * Config)164 static GSM_Error SMSDDBI_Connect(GSM_SMSDConfig * Config)
165 {
166 int rc;
167 struct GSM_SMSDdbobj *db = Config->db;
168
169 rc = dbi_initialize(Config->driverspath);
170
171 if (rc == 0) {
172 SMSD_Log(DEBUG_ERROR, Config, "DBI did not find any drivers, try using DriversPath option");
173 dbi_shutdown();
174 return ERR_DB_DRIVER;
175 } else if (rc < 0) {
176 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to initialize!");
177 return ERR_DB_DRIVER;
178 }
179
180 Config->conn.dbi = dbi_conn_new(Config->driver);
181 if (Config->conn.dbi == NULL) {
182 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to init %s driver!", Config->driver);
183 dbi_shutdown();
184 return ERR_DB_DRIVER;
185 } else {
186 SMSD_Log(DEBUG_SQL, Config, "Using DBI driver '%s'", dbi_driver_get_name(dbi_conn_get_driver(Config->conn.dbi)));
187 }
188
189 dbi_conn_error_handler(Config->conn.dbi, SMSDDBI_Callback, Config);
190
191 if (dbi_conn_set_option(Config->conn.dbi, "sqlite_dbdir", Config->dbdir) != 0) {
192 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set sqlite_dbdir!");
193 SMSDDBI_Free(Config);
194 return ERR_DB_CONFIG;
195 }
196 if (dbi_conn_set_option(Config->conn.dbi, "sqlite3_dbdir", Config->dbdir) != 0) {
197 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set sqlite3_dbdir!");
198 SMSDDBI_Free(Config);
199 return ERR_DB_CONFIG;
200 }
201 if (dbi_conn_set_option(Config->conn.dbi, "host", Config->host) != 0) {
202 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set host!");
203 SMSDDBI_Free(Config);
204 return ERR_DB_CONFIG;
205 }
206 if (dbi_conn_set_option(Config->conn.dbi, "username", Config->user) != 0) {
207 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set username!");
208 SMSDDBI_Free(Config);
209 return ERR_DB_CONFIG;
210 }
211 if (dbi_conn_set_option(Config->conn.dbi, "password", Config->password) != 0) {
212 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set password!");
213 SMSDDBI_Free(Config);
214 return ERR_DB_CONFIG;
215 }
216 if (dbi_conn_set_option(Config->conn.dbi, "dbname", Config->database) != 0) {
217 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set dbname!");
218 SMSDDBI_Free(Config);
219 return ERR_DB_CONFIG;
220 }
221 if (dbi_conn_set_option(Config->conn.dbi, "encoding", "UTF-8") != 0) {
222 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set encoding!");
223 SMSDDBI_Free(Config);
224 return ERR_DB_CONFIG;
225 }
226
227 if (dbi_conn_connect(Config->conn.dbi) != 0) {
228 SMSD_Log(DEBUG_ERROR, Config, "DBI failed to connect!");
229 SMSDDBI_Free(Config);
230 return ERR_DB_CONNECT;
231 }
232 Config->db = db;
233 return ERR_NONE;
234 }
235
SMSDDBI_Query(GSM_SMSDConfig * Config,const char * query,SQL_result * res)236 static GSM_Error SMSDDBI_Query(GSM_SMSDConfig * Config, const char *query, SQL_result * res)
237 {
238 const char *msg;
239 int rc;
240
241 res->dbi = NULL;
242
243 res->dbi = dbi_conn_query(Config->conn.dbi, query);
244 if (res->dbi != NULL)
245 return ERR_NONE;
246
247 SMSD_Log(DEBUG_INFO, Config, "SQL failed: %s", query);
248 /* Black magic to decide whether we should bail out or attempt to retry */
249 rc = dbi_conn_error(Config->conn.dbi, &msg);
250 if (rc != -1) {
251 SMSD_Log(DEBUG_INFO, Config, "SQL failure: %s", msg);
252 if (strstr(msg, "syntax") != NULL) {
253 return ERR_SQL;
254 }
255 if (strstr(msg, "violation") != NULL) {
256 return ERR_SQL;
257 }
258 if (strstr(msg, "violates") != NULL) {
259 return ERR_SQL;
260 }
261 if (strstr(msg, "SQL error") != NULL) {
262 return ERR_SQL;
263 }
264 if (strstr(msg, "duplicate") != NULL) {
265 return ERR_SQL;
266 }
267 if (strstr(msg, "unique") != NULL) {
268 return ERR_SQL;
269 }
270 if (strstr(msg, "need to rewrite") != NULL) {
271 return ERR_SQL;
272 }
273 if (strstr(msg, "locked") != NULL) {
274 return ERR_DB_TIMEOUT;
275 }
276 }
277 return ERR_DB_TIMEOUT;
278 }
279
280 /* free sql results */
SMSDDBI_FreeResult(GSM_SMSDConfig * Config,SQL_result * res)281 void SMSDDBI_FreeResult(GSM_SMSDConfig * Config, SQL_result *res)
282 {
283 dbi_result_free(res->dbi);
284 }
285
286 /* set pointer to next row */
SMSDDBI_NextRow(GSM_SMSDConfig * Config,SQL_result * res)287 int SMSDDBI_NextRow(GSM_SMSDConfig * Config, SQL_result *res)
288 {
289 return dbi_result_next_row(res->dbi);
290 }
291 /* quote strings */
SMSDDBI_QuoteString(GSM_SMSDConfig * Config,const char * string)292 char * SMSDDBI_QuoteString(GSM_SMSDConfig * Config, const char *string)
293 {
294 char *encoded_text = NULL;
295 dbi_conn_quote_string_copy(Config->conn.dbi, string, &encoded_text);
296 return encoded_text;
297 }
298 /* LAST_INSERT_ID */
SMSDDBI_SeqID(GSM_SMSDConfig * Config,const char * id)299 unsigned long long SMSDDBI_SeqID(GSM_SMSDConfig * Config, const char *id)
300 {
301 unsigned long long new_id;
302 char buffer[100];
303
304 new_id = dbi_conn_sequence_last(Config->conn.dbi, NULL);
305 if (new_id == 0) {
306 new_id = dbi_conn_sequence_last(Config->conn.dbi, id);
307 /* Need to escape for PostgreSQL */
308 if (new_id == 0) {
309 sprintf(buffer, "\"%s\"", id);
310 new_id = dbi_conn_sequence_last(Config->conn.dbi, buffer);
311 }
312 }
313 return new_id;
314 }
315
SMSDDBI_AffectedRows(GSM_SMSDConfig * Config,SQL_result * res)316 unsigned long SMSDDBI_AffectedRows(GSM_SMSDConfig * Config, SQL_result *res)
317 {
318 return dbi_result_get_numrows_affected(res->dbi);
319 }
320
321 struct GSM_SMSDdbobj SMSDDBI = {
322 SMSDDBI_Connect,
323 SMSDDBI_Query,
324 SMSDDBI_Free,
325 SMSDDBI_FreeResult,
326 SMSDDBI_NextRow,
327 SMSDDBI_SeqID,
328 SMSDDBI_AffectedRows,
329 SMSDDBI_GetString,
330 SMSDDBI_GetNumber,
331 SMSDDBI_GetDate,
332 SMSDDBI_GetBool,
333 SMSDDBI_QuoteString,
334 };
335
336 /* How should editor hadle tabs in this file? Add editor commands here.
337 * vim: noexpandtab sw=8 ts=8 sts=8:
338 */
339