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