1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Anatol Belski <ab@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22
23 #ifdef DBA_LMDB
24 #include "php_lmdb.h"
25
26 #ifdef LMDB_INCLUDE_FILE
27 #include LMDB_INCLUDE_FILE
28 #endif
29
30 struct php_lmdb_info {
31 MDB_env *env;
32 MDB_txn *txn;
33 MDB_dbi dbi;
34 MDB_cursor *cur;
35 };
36
37 #define LMDB_IT(it) (((struct php_lmdb_info *)info->dbf)->it)
38
DBA_OPEN_FUNC(lmdb)39 DBA_OPEN_FUNC(lmdb)
40 {
41 MDB_env *env;
42 MDB_txn *txn;
43 int rc;
44 int mode = info->file_permission;
45 zend_long map_size = info->map_size;
46
47 ZEND_ASSERT(map_size >= 0);
48
49 /* By default use the MDB_NOSUBDIR flag */
50 int flags = MDB_NOSUBDIR;
51 /* Use flags passed by the user for driver flags */
52 if (info->driver_flags != DBA_DEFAULT_DRIVER_FLAGS) {
53 ZEND_ASSERT(info->driver_flags >= 0);
54 switch (info->driver_flags) {
55 case 0:
56 case MDB_NOSUBDIR:
57 flags = info->driver_flags;
58 break;
59 default:
60 zend_argument_value_error(6, "must be either DBA_LMDB_USE_SUB_DIR or DBA_LMDB_NO_SUB_DIR for LMDB driver");
61 return FAILURE;
62 }
63 }
64
65 /* Add readonly flag if DB is opened in read only mode */
66 if (info->mode == DBA_READER) {
67 flags |= MDB_RDONLY;
68 }
69
70 rc = mdb_env_create(&env);
71 if (rc) {
72 *error = mdb_strerror(rc);
73 return FAILURE;
74 }
75
76 if (map_size > 0) {
77 rc = mdb_env_set_mapsize(env, (size_t) map_size);
78 if (rc) {
79 *error = mdb_strerror(rc);
80 return FAILURE;
81 }
82 }
83
84 rc = mdb_env_open(env, info->path, flags, mode);
85 if (rc) {
86 /* If this function [mdb_env_open()] fails, mdb_env_close() must be called to discard the MDB_env handle.
87 * http://www.lmdb.tech/doc/group__mdb.html#ga32a193c6bf4d7d5c5d579e71f22e9340 */
88 mdb_env_close(env);
89 *error = mdb_strerror(rc);
90 return FAILURE;
91 }
92
93 rc = mdb_txn_begin(env, NULL, /* flags */ MDB_RDONLY, &txn);
94 if (rc) {
95 mdb_env_close(env);
96 *error = mdb_strerror(rc);
97 return FAILURE;
98 }
99
100 info->dbf = pemalloc(sizeof(struct php_lmdb_info), info->flags & DBA_PERSISTENT);
101 if (!info->dbf) {
102 *error = "Failed to allocate php_lmdb_info.";
103 return FAILURE;
104 }
105 memset(info->dbf, 0, sizeof(struct php_lmdb_info));
106
107 rc = mdb_dbi_open(txn, NULL, 0, &LMDB_IT(dbi));
108 if (rc) {
109 mdb_env_close(env);
110 pefree(info->dbf, info->flags & DBA_PERSISTENT);
111 *error = mdb_strerror(rc);
112 return FAILURE;
113 }
114
115 LMDB_IT(env) = env;
116 LMDB_IT(txn) = txn;
117
118 mdb_txn_abort(LMDB_IT(txn));
119
120 return SUCCESS;
121 }
122
DBA_CLOSE_FUNC(lmdb)123 DBA_CLOSE_FUNC(lmdb)
124 {
125 mdb_dbi_close(LMDB_IT(env), LMDB_IT(dbi));
126 mdb_env_close(LMDB_IT(env));
127
128 pefree(info->dbf, info->flags & DBA_PERSISTENT);
129 }
130
DBA_FETCH_FUNC(lmdb)131 DBA_FETCH_FUNC(lmdb)
132 {
133 int rc;
134 MDB_val k, v;
135 zend_string *ret = NULL;
136
137 if (LMDB_IT(cur)) {
138 rc = mdb_txn_renew(LMDB_IT(txn));
139 } else {
140 rc = mdb_txn_begin(LMDB_IT(env), NULL, MDB_RDONLY, &LMDB_IT(txn));
141 }
142 if (rc) {
143 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
144 return NULL;
145 }
146
147 k.mv_size = ZSTR_LEN(key);
148 k.mv_data = ZSTR_VAL(key);
149
150 rc = mdb_get(LMDB_IT(txn), LMDB_IT(dbi), &k, &v);
151 if (rc) {
152 if (MDB_NOTFOUND != rc) {
153 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
154 }
155 mdb_txn_abort(LMDB_IT(txn));
156 return NULL;
157 }
158
159 if (v.mv_data) {
160 ret = zend_string_init(v.mv_data, v.mv_size, /* persistent */ false);
161 }
162
163 if (LMDB_IT(cur)) {
164 mdb_txn_reset(LMDB_IT(txn));
165 } else {
166 mdb_txn_abort(LMDB_IT(txn));
167 }
168
169 return ret;
170 }
171
DBA_UPDATE_FUNC(lmdb)172 DBA_UPDATE_FUNC(lmdb)
173 {
174 int rc;
175 MDB_val k, v;
176
177 rc = mdb_txn_begin(LMDB_IT(env), NULL, 0, &LMDB_IT(txn));
178 if (rc) {
179 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
180 return FAILURE;
181 }
182
183 k.mv_size = ZSTR_LEN(key);
184 k.mv_data = ZSTR_VAL(key);
185 v.mv_size = ZSTR_LEN(val);
186 v.mv_data = ZSTR_VAL(val);
187
188 rc = mdb_put(LMDB_IT(txn), LMDB_IT(dbi), &k, &v, mode == 1 ? MDB_NOOVERWRITE : 0);
189 if (rc) {
190 if (MDB_KEYEXIST != rc) {
191 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
192 }
193 mdb_txn_abort(LMDB_IT(txn));
194 return FAILURE;
195 }
196
197 rc = mdb_txn_commit(LMDB_IT(txn));
198 if (rc) {
199 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
200 mdb_txn_abort(LMDB_IT(txn));
201 return FAILURE;
202 }
203
204 return SUCCESS;
205 }
206
DBA_EXISTS_FUNC(lmdb)207 DBA_EXISTS_FUNC(lmdb)
208 {
209 int rc;
210 MDB_val k, v;
211
212 if (LMDB_IT(cur)) {
213 rc = mdb_txn_renew(LMDB_IT(txn));
214 } else {
215 rc = mdb_txn_begin(LMDB_IT(env), NULL, MDB_RDONLY, &LMDB_IT(txn));
216 }
217 if (rc) {
218 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
219 return FAILURE;
220 }
221
222 k.mv_size = ZSTR_LEN(key);
223 k.mv_data = ZSTR_VAL(key);
224
225 rc = mdb_get(LMDB_IT(txn), LMDB_IT(dbi), &k, &v);
226 if (rc) {
227 if (MDB_NOTFOUND != rc) {
228 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
229 }
230 mdb_txn_abort(LMDB_IT(txn));
231 return FAILURE;
232 }
233
234 if (LMDB_IT(cur)) {
235 mdb_txn_reset(LMDB_IT(txn));
236 } else {
237 mdb_txn_abort(LMDB_IT(txn));
238 }
239
240 return SUCCESS;
241 }
242
DBA_DELETE_FUNC(lmdb)243 DBA_DELETE_FUNC(lmdb)
244 {
245 int rc;
246 MDB_val k;
247
248 rc = mdb_txn_begin(LMDB_IT(env), NULL, 0, &LMDB_IT(txn));
249 if (rc) {
250 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
251 return FAILURE;
252 }
253
254 k.mv_size = ZSTR_LEN(key);
255 k.mv_data = ZSTR_VAL(key);
256
257 rc = mdb_del(LMDB_IT(txn), LMDB_IT(dbi), &k, NULL);
258 if (!rc) {
259 rc = mdb_txn_commit(LMDB_IT(txn));
260 if (rc) {
261 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
262 mdb_txn_abort(LMDB_IT(txn));
263 return FAILURE;
264 }
265 return SUCCESS;
266 }
267
268 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
269
270 mdb_txn_abort(LMDB_IT(txn));
271 return FAILURE;
272 }
273
DBA_FIRSTKEY_FUNC(lmdb)274 DBA_FIRSTKEY_FUNC(lmdb)
275 {
276 int rc;
277 MDB_val k, v;
278 zend_string *ret = NULL;
279
280 rc = mdb_txn_begin(LMDB_IT(env), NULL, MDB_RDONLY, &LMDB_IT(txn));
281 if (rc) {
282 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
283 return NULL;
284 }
285
286 rc = mdb_cursor_open(LMDB_IT(txn), LMDB_IT(dbi), &LMDB_IT(cur));
287 if (rc) {
288 mdb_txn_abort(LMDB_IT(txn));
289 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
290 return NULL;
291 }
292
293 rc = mdb_cursor_get(LMDB_IT(cur), &k, &v, MDB_FIRST);
294 if (rc) {
295 mdb_txn_abort(LMDB_IT(txn));
296 mdb_cursor_close(LMDB_IT(cur));
297 LMDB_IT(cur) = NULL;
298 if (MDB_NOTFOUND != rc) {
299 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
300 }
301 return NULL;
302 }
303
304 if (k.mv_data) {
305 ret = zend_string_init(k.mv_data, k.mv_size, /* persistent */ false);
306 }
307
308 mdb_txn_reset(LMDB_IT(txn));
309
310 return ret;
311 }
312
DBA_NEXTKEY_FUNC(lmdb)313 DBA_NEXTKEY_FUNC(lmdb)
314 {
315 int rc;
316 MDB_val k, v;
317 zend_string *ret = NULL;
318
319 rc = mdb_txn_renew(LMDB_IT(txn));
320 if (rc) {
321 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
322 return NULL;
323 }
324
325 rc = mdb_cursor_get(LMDB_IT(cur), &k, &v, MDB_NEXT);
326 if (rc) {
327 mdb_txn_abort(LMDB_IT(txn));
328 mdb_cursor_close(LMDB_IT(cur));
329 LMDB_IT(cur) = NULL;
330 if (MDB_NOTFOUND != rc) {
331 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
332 }
333 return NULL;
334 }
335
336 if (k.mv_data) {
337 ret = zend_string_init(k.mv_data, k.mv_size, /* persistent */ false);
338 }
339
340 mdb_txn_reset(LMDB_IT(txn));
341
342 return ret;
343 }
344
DBA_OPTIMIZE_FUNC(lmdb)345 DBA_OPTIMIZE_FUNC(lmdb)
346 {
347 return SUCCESS;
348 }
349
DBA_SYNC_FUNC(lmdb)350 DBA_SYNC_FUNC(lmdb)
351 {
352 int rc;
353
354 rc = mdb_env_sync(LMDB_IT(env), 1);
355 if (rc) {
356 php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
357 return FAILURE;
358 }
359
360 return SUCCESS;
361 }
362
DBA_INFO_FUNC(lmdb)363 DBA_INFO_FUNC(lmdb)
364 {
365 return estrdup(MDB_VERSION_STRING);
366 }
367
368 #endif
369