1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2020 - 2024 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
10 /* This header file contains macro definitions for one possible hintsdb
13 /* ********************* sqlite3 interface ************************ */
18 # define EXIM_DB sqlite3
20 # define EXIM_CURSOR int
22 # /* The datum type used for queries */
23 # define EXIM_DATUM blob
25 /* Some text for messages */
26 # define EXIM_DBTYPE "sqlite3"
28 # /* Access functions */
31 exim_lockfile_needed(void)
33 return FALSE; /* We do transaction; no extra locking needed */
36 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
37 static inline EXIM_DB *
38 exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
42 int ret, sflags = flags & O_RDWR ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
43 if (flags & O_CREAT) sflags |= SQLITE_OPEN_CREATE;
44 if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK)
46 sqlite3_busy_timeout(dbp, 5000);
48 ret = sqlite3_exec(dbp,
49 "CREATE TABLE IF NOT EXISTS tbl (ky TEXT PRIMARY KEY, dat BLOB);",
55 // fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errmsg(dbp));
56 return ret == SQLITE_OK ? dbp : NULL;
60 exim_dbtransaction_start(EXIM_DB * dbp)
62 return sqlite3_exec(dbp, "BEGIN TRANSACTION;", NULL, NULL, NULL) == SQLITE_OK;
65 static inline EXIM_DB *
66 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
69 EXIM_DB * dbp = exim_dbopen_multi(name, dirname, flags, mode);
70 if (!dbp || exim_dbtransaction_start(dbp))
76 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
77 /* note we alloc'n'copy - the caller need not do so */
78 /* result has a NUL appended, but the length is as per the DB */
81 exim_dbget__(EXIM_DB * dbp, const uschar * s, EXIM_DATUM * res)
83 sqlite3_stmt * statement;
86 res->len = (size_t) -1;
87 /* fprintf(stderr, "exim_dbget__(%s)\n", s); */
88 if ((ret = sqlite3_prepare_v2(dbp, CCS s, -1, &statement, NULL)) != SQLITE_OK)
90 /* fprintf(stderr, "prepare fail: %s\n", sqlite3_errmsg(dbp)); */
93 if (sqlite3_step(statement) != SQLITE_ROW)
95 /* fprintf(stderr, "step fail: %s\n", sqlite3_errmsg(dbp)); */
96 sqlite3_finalize(statement);
100 res->len = sqlite3_column_bytes(statement, 0);
101 # ifdef COMPILE_UTILITY
102 if (!(res->data = malloc(res->len +1)))
103 { sqlite3_finalize(statement); return FALSE; }
105 res->data = store_get(res->len +1, GET_TAINTED);
107 memcpy(res->data, sqlite3_column_blob(statement, 0), res->len);
108 res->data[res->len] = '\0';
109 /* fprintf(stderr, "res %d bytes: '%.*s'\n", (int)res->len, (int)res->len, res->data); */
110 sqlite3_finalize(statement);
115 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
117 # define FMT "SELECT dat FROM tbl WHERE ky = '%.*s';"
122 # ifdef COMPILE_UTILITY
123 /* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
124 i = snprintf(NULL, 0, FMT, (int) key->len, key->data)+1;
125 if (!(qry = malloc(i)))
127 snprintf(CS qry, i, FMT, (int) key->len, key->data);
128 ret = exim_dbget__(dbp, qry, res);
131 /* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
132 qry = string_sprintf(FMT, (int) key->len, key->data);
133 ret = exim_dbget__(dbp, qry, res);
140 /* Note that we return claiming a duplicate record for any error.
141 It seem not uncommon to get a "database is locked" error. */
142 # define EXIM_DBPUTB_OK 0
143 # define EXIM_DBPUTB_DUP (-1)
146 exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * alt)
148 int hlen = data->len * 2, off = 0, res;
149 # define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');"
151 # ifdef COMPILE_UTILITY
152 uschar * hex = malloc(hlen+1);
153 if (!hex) return EXIM_DBPUTB_DUP; /* best we can do */
155 uschar * hex = store_get(hlen+1, data->data);
158 for (const uschar * s = data->data, * t = s + data->len; s < t; s++, off += 2)
159 sprintf(CS hex + off, "%02X", *s);
161 # ifdef COMPILE_UTILITY
162 res = snprintf(CS hex, 0, FMT, alt, (int) key->len, key->data, hlen, hex) +1;
163 if (!(qry = malloc(res))) return EXIM_DBPUTB_DUP;
164 snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, hlen, hex);
165 /* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
166 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
170 qry = string_sprintf(FMT, alt, (int) key->len, key->data, hlen, hex);
171 /* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
172 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
173 /* fprintf(stderr, "exim_s_dbp res %d\n", res); */
176 # ifdef COMPILE_UTILITY
177 if (res != SQLITE_OK)
178 fprintf(stderr, "sqlite3_exec: %s\n", sqlite3_errmsg(dbp));
181 return res == SQLITE_OK ? EXIM_DBPUTB_OK : EXIM_DBPUTB_DUP;
185 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
188 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
190 /* fprintf(stderr, "exim_dbput()\n"); */
191 (void) exim_s_dbp(dbp, key, data, US"REPLACE");
195 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
197 /* Returns from EXIM_DBPUTB */
200 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
202 return exim_s_dbp(dbp, key, data, US"ABORT");
207 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
209 # define FMT "DELETE FROM tbl WHERE ky = '%.*s';"
213 # ifdef COMPILE_UTILITY
214 res = snprintf(NULL, 0, FMT, (int) key->len, key->data) +1; /* res includes nul */
215 if (!(qry = malloc(res))) return SQLITE_NOMEM;
216 snprintf(CS qry, res, FMT, (int) key->len, key->data);
217 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
220 qry = string_sprintf(FMT, (int) key->len, key->data);
221 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
229 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
230 /* Cursors are inefficiently emulated by repeating searches */
232 static inline EXIM_CURSOR *
233 exim_dbcreate_cursor(EXIM_DB * dbp)
235 # ifdef COMPILE_UTILITY
236 EXIM_CURSOR * c = malloc(sizeof(int));
239 EXIM_CURSOR * c = store_malloc(sizeof(int));
246 /* Note that we return the (next) key, not the record value */
248 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first,
249 EXIM_CURSOR * cursor)
251 # define FMT "SELECT ky FROM tbl ORDER BY ky LIMIT 1 OFFSET %d;"
256 # ifdef COMPILE_UTILITY
257 i = snprintf(NULL, 0, FMT, *cursor)+1;
258 if (!(qry = malloc(i))) return FALSE;
259 snprintf(CS qry, i, FMT, *cursor);
260 /* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
261 ret = exim_dbget__(dbp, qry, key);
263 /* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
265 qry = string_sprintf(FMT, *cursor);
266 /* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
267 ret = exim_dbget__(dbp, qry, key);
268 /* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
270 if (ret) *cursor = *cursor + 1;
275 /* EXIM_DBDELETE_CURSOR - terminate scanning operation. */
277 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
279 # ifdef COMPILE_UTILITY
289 exim_dbclose_multi(EXIM_DB * dbp)
294 exim_dbtransaction_commit(EXIM_DB * dbp)
296 (void) sqlite3_exec(dbp, "COMMIT TRANSACTION;", NULL, NULL, NULL);
299 exim_dbclose__(EXIM_DB * dbp)
301 exim_dbtransaction_commit(dbp);
302 exim_dbclose_multi(dbp);
309 exim_datum_data_get(EXIM_DATUM * dp)
310 { return US dp->data; }
312 exim_datum_data_set(EXIM_DATUM * dp, void * s)
316 exim_datum_size_get(EXIM_DATUM * dp)
319 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
325 exim_datum_init(EXIM_DATUM * dp)
326 { dp->data = NULL; } /* compiler quietening */
328 /* No free needed for a datum */
331 exim_datum_free(EXIM_DATUM * dp)
336 # define EXIM_DB_RLIMIT 150
339 /* End of hints_sqlite.h */