diff options
-rw-r--r-- | ext/dba/config.m4 | 34 | ||||
-rw-r--r-- | ext/dba/config.w32 | 13 | ||||
-rw-r--r-- | ext/dba/dba.c | 6 | ||||
-rw-r--r-- | ext/dba/dba_lmdb.c | 353 | ||||
-rw-r--r-- | ext/dba/php_lmdb.h | 12 | ||||
-rw-r--r-- | ext/dba/tests/clean.inc | 1 | ||||
-rw-r--r-- | ext/dba/tests/dba_lmdb.phpt | 38 |
7 files changed, 457 insertions, 0 deletions
diff --git a/ext/dba/config.m4 b/ext/dba/config.m4 index 740cf14e39..a8bd561c50 100644 --- a/ext/dba/config.m4 +++ b/ext/dba/config.m4 @@ -100,6 +100,9 @@ PHP_ARG_WITH(dbm,, PHP_ARG_WITH(tcadb,, [ --with-tcadb[=DIR] DBA: Tokyo Cabinet abstract DB support], no, no) +PHP_ARG_WITH(lmdb,, +[ --with-lmdb[=DIR] DBA: Lightning memory-mapped database support], no, no) + dnl dnl Library checks @@ -228,6 +231,37 @@ if test "$PHP_TCADB" != "no"; then fi PHP_DBA_STD_RESULT(tcadb) +dnl LMDB +if test "$PHP_LMDB" != "no"; then + PHP_DBA_STD_BEGIN + for i in $PHP_LMDB /usr/local /usr; do + if test -f "$i/include/lmdb.h"; then + THIS_PREFIX=$i + PHP_ADD_INCLUDE($THIS_PREFIX/include) + THIS_INCLUDE=$i/include/lmdb.h + break + fi + done + + if test -n "$THIS_INCLUDE"; then + for LIB in lmdb; do + PHP_CHECK_LIBRARY($LIB, mdb_open, [ + AC_DEFINE_UNQUOTED(LMDB_INCLUDE_FILE, "$THIS_INCLUDE", [ ]) + AC_DEFINE(DBA_LMDB, 1, [ ]) + THIS_LIBS=$LIB + ], [], [-L$THIS_PREFIX/$PHP_LIBDIR]) + if test -n "$THIS_LIBS"; then + break + fi + done + fi + + PHP_DBA_STD_ASSIGN + PHP_DBA_STD_CHECK + PHP_DBA_STD_ATTACH +fi +PHP_DBA_STD_RESULT(lmdb) + dnl Berkeley specific (library and version test) dnl parameters(version, library list, function) AC_DEFUN([PHP_DBA_DB_CHECK],[ diff --git a/ext/dba/config.w32 b/ext/dba/config.w32 index d521e6ad62..5eb542d986 100644 --- a/ext/dba/config.w32 +++ b/ext/dba/config.w32 @@ -4,6 +4,7 @@ ARG_WITH("dba", "DBA support", "no"); ARG_WITH("qdbm", "DBA: QDBM support", "no"); ARG_WITH("db", "DBA: Berkeley DB support", "no"); +ARG_WITH("lmdb", "DBA: Lightning memory-mapped database support", "no"); if (PHP_DBA != "no") { EXTENSION("dba", "dba.c dba_cdb.c dba_db1.c dba_db2.c dba_db3.c dba_dbm.c dba_flatfile.c dba_gdbm.c dba_ndbm.c dba_inifile.c"); @@ -32,4 +33,16 @@ if (PHP_DBA != "no") { WARNING("dba: qdbm handlers not enabled; libraries and headers not found"); } } + + if (PHP_QDBM != "no") { + if (CHECK_LIB("liblmdb_a.lib", "dba", PHP_DBA) && + CHECK_HEADER_ADD_INCLUDE("lmdb.h", "CFLAGS_DBA") && + CHECK_LIB("ntdll.lib", "dba", PHP_DBA)) { + ADD_SOURCES("ext/dba", "dba_lmdb.c", "dba"); + AC_DEFINE("LMDB_INCLUDE_FILE", "<lmdb.h>", "", false); + AC_DEFINE("DBA_LMDB", 1, ""); + } else { + WARNING("dba: lmdb handlers not enabled; libraries and headers not found"); + } + } } diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 072f84b784..a8470d9d59 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -51,6 +51,7 @@ #include "php_inifile.h" #include "php_qdbm.h" #include "php_tcadb.h" +#include "php_lmdb.h" /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_dba_popen, 0, 0, 2) @@ -363,6 +364,9 @@ static dba_handler handler[] = { #if DBA_TCADB DBA_HND(tcadb, DBA_LOCK_ALL) #endif +#if DBA_LMDB + DBA_HND(lmdb, DBA_LOCK_EXT) +#endif { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -387,6 +391,8 @@ static dba_handler handler[] = { #elif DBA_TCADB #define DBA_DEFAULT "tcadb" #else +#define DBA_DEFAULT "lmdb" +#else #define DBA_DEFAULT "" #endif /* cdb/cdb_make and ini are no option here */ diff --git a/ext/dba/dba_lmdb.c b/ext/dba/dba_lmdb.c new file mode 100644 index 0000000000..91f42e1a76 --- /dev/null +++ b/ext/dba/dba_lmdb.c @@ -0,0 +1,353 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 2017 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Anatol Belski <ab@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if DBA_LMDB +#include "php_lmdb.h" + +#ifdef LMDB_INCLUDE_FILE +#include LMDB_INCLUDE_FILE +#endif + +struct php_lmdb_info { + MDB_env *env; + MDB_txn *txn; + MDB_dbi dbi; + MDB_cursor *cur; +}; + +#define LMDB_IT(it) (((struct php_lmdb_info *)info->dbf)->it) + +DBA_OPEN_FUNC(lmdb) +{ + MDB_env *env; + MDB_txn *txn; + int rc, mode = 0644, flags = MDB_NOSUBDIR; + + if(info->argc > 0) { + convert_to_long_ex(&info->argv[0]); + mode = Z_LVAL(info->argv[0]); + + /* TODO implement handling of the additional flags. */ + } + + rc = mdb_env_create(&env); + if (rc) { + *error = mdb_strerror(rc); + return FAILURE; + } + + rc = mdb_env_open(env, info->path, flags, mode); + if (rc) { + *error = mdb_strerror(rc); + return FAILURE; + } + + rc = mdb_txn_begin(env, NULL, 0, &txn); + if (rc) { + mdb_env_close(env); + *error = mdb_strerror(rc); + return FAILURE; + } + + info->dbf = pemalloc(sizeof(struct php_lmdb_info), info->flags & DBA_PERSISTENT); + if (!info->dbf) { + *error = "Failed to allocate php_lmdb_info."; + return FAILURE; + } + memset(info->dbf, 0, sizeof(struct php_lmdb_info)); + + rc = mdb_dbi_open(txn, NULL, 0, &LMDB_IT(dbi)); + if (rc) { + mdb_env_close(env); + pefree(info->dbf, info->flags & DBA_PERSISTENT); + *error = mdb_strerror(rc); + return FAILURE; + } + + LMDB_IT(env) = env; + LMDB_IT(txn) = txn; + + mdb_txn_abort(LMDB_IT(txn)); + + return SUCCESS; +} + +DBA_CLOSE_FUNC(lmdb) +{ + mdb_dbi_close(LMDB_IT(env), LMDB_IT(dbi)); + mdb_env_close(LMDB_IT(env)); + + pefree(info->dbf, info->flags & DBA_PERSISTENT); +} + +DBA_FETCH_FUNC(lmdb) +{ + int rc; + MDB_val k, v; + char *ret = NULL; + + if (LMDB_IT(cur)) { + rc = mdb_txn_renew(LMDB_IT(txn)); + } else { + rc = mdb_txn_begin(LMDB_IT(env), NULL, MDB_RDONLY, &LMDB_IT(txn)); + } + if (rc) { + php_error_docref1(NULL, key, E_WARNING, "%s", mdb_strerror(rc)); + return NULL; + } + + k.mv_size = keylen; + k.mv_data = key; + + rc = mdb_get(LMDB_IT(txn), LMDB_IT(dbi), &k, &v); + if (rc) { + if (MDB_NOTFOUND != rc) { + php_error_docref1(NULL, key, E_WARNING, "%s", mdb_strerror(rc)); + } + mdb_txn_abort(LMDB_IT(txn)); + return NULL; + } + + if (v.mv_data) { + if(newlen) *newlen = v.mv_size; + ret = estrndup(v.mv_data, v.mv_size); + } + + if (LMDB_IT(cur)) { + mdb_txn_reset(LMDB_IT(txn)); + } else { + mdb_txn_abort(LMDB_IT(txn)); + } + + return ret; +} + +DBA_UPDATE_FUNC(lmdb) +{ + int rc; + MDB_val k, v; + + rc = mdb_txn_begin(LMDB_IT(env), NULL, 0, &LMDB_IT(txn)); + if (rc) { + php_error_docref2(NULL, key, val, E_WARNING, "%s", mdb_strerror(rc)); + return FAILURE; + } + + k.mv_size = keylen; + k.mv_data = key; + v.mv_size = vallen; + v.mv_data = val; + + rc = mdb_put(LMDB_IT(txn), LMDB_IT(dbi), &k, &v, mode == 1 ? MDB_NOOVERWRITE : 0); + if (rc) { + if (MDB_KEYEXIST != rc) { + php_error_docref2(NULL, key, val, E_WARNING, "%s", mdb_strerror(rc)); + } + mdb_txn_abort(LMDB_IT(txn)); + return FAILURE; + } + + rc = mdb_txn_commit(LMDB_IT(txn)); + if (rc) { + php_error_docref2(NULL, key, val, E_WARNING, "%s", mdb_strerror(rc)); + mdb_txn_abort(LMDB_IT(txn)); + return FAILURE; + } + + return SUCCESS; +} + +DBA_EXISTS_FUNC(lmdb) +{ + int rc; + MDB_val k, v; + + if (LMDB_IT(cur)) { + rc = mdb_txn_renew(LMDB_IT(txn)); + } else { + rc = mdb_txn_begin(LMDB_IT(env), NULL, MDB_RDONLY, &LMDB_IT(txn)); + } + if (rc) { + php_error_docref1(NULL, key, E_WARNING, "%s", mdb_strerror(rc)); + return FAILURE; + } + + k.mv_size = keylen; + k.mv_data = key; + + rc = mdb_get(LMDB_IT(txn), LMDB_IT(dbi), &k, &v); + if (rc) { + if (MDB_NOTFOUND != rc) { + php_error_docref1(NULL, key, E_WARNING, "%s", mdb_strerror(rc)); + } + mdb_txn_abort(LMDB_IT(txn)); + return FAILURE; + } + + if (LMDB_IT(cur)) { + mdb_txn_reset(LMDB_IT(txn)); + } else { + mdb_txn_abort(LMDB_IT(txn)); + } + + return SUCCESS; +} + +DBA_DELETE_FUNC(lmdb) +{ + int rc; + MDB_val k; + + rc = mdb_txn_begin(LMDB_IT(env), NULL, 0, &LMDB_IT(txn)); + if (rc) { + php_error_docref1(NULL, key, E_WARNING, "%s", mdb_strerror(rc)); + return FAILURE; + } + + k.mv_size = keylen; + k.mv_data = key; + + rc = mdb_del(LMDB_IT(txn), LMDB_IT(dbi), &k, NULL); + if (!rc) { + rc = mdb_txn_commit(LMDB_IT(txn)); + if (rc) { + php_error_docref1(NULL, key, E_WARNING, "%s", mdb_strerror(rc)); + mdb_txn_abort(LMDB_IT(txn)); + return FAILURE; + } + return SUCCESS; + } + + php_error_docref1(NULL, key, E_WARNING, "%s", mdb_strerror(rc)); + + return FAILURE; +} + +DBA_FIRSTKEY_FUNC(lmdb) +{ + int rc; + MDB_val k, v; + char *ret = NULL; + + rc = mdb_txn_begin(LMDB_IT(env), NULL, MDB_RDONLY, &LMDB_IT(txn)); + if (rc) { + php_error_docref0(NULL, E_WARNING, "%s", mdb_strerror(rc)); + return NULL; + } + + rc = mdb_cursor_open(LMDB_IT(txn), LMDB_IT(dbi), &LMDB_IT(cur)); + if (rc) { + mdb_txn_abort(LMDB_IT(txn)); + php_error_docref0(NULL, E_WARNING, "%s", mdb_strerror(rc)); + return NULL; + } + + rc = mdb_cursor_get(LMDB_IT(cur), &k, &v, MDB_FIRST); + if (rc) { + mdb_txn_abort(LMDB_IT(txn)); + mdb_cursor_close(LMDB_IT(cur)); + if (MDB_NOTFOUND != rc) { + php_error_docref0(NULL, E_WARNING, "%s", mdb_strerror(rc)); + } + return NULL; + } + + if(k.mv_data) { + if(newlen) *newlen = k.mv_size; + ret = estrndup(k.mv_data, k.mv_size); + } + + mdb_txn_reset(LMDB_IT(txn)); + + return ret; +} + +DBA_NEXTKEY_FUNC(lmdb) +{ + int rc; + MDB_val k, v; + char *ret = NULL; + + rc = mdb_txn_renew(LMDB_IT(txn)); + if (rc) { + php_error_docref0(NULL, E_WARNING, "%s", mdb_strerror(rc)); + return NULL; + } + + rc = mdb_cursor_get(LMDB_IT(cur), &k, &v, MDB_NEXT); + if (rc) { + mdb_txn_abort(LMDB_IT(txn)); + mdb_cursor_close(LMDB_IT(cur)); + LMDB_IT(cur) = NULL; + if (MDB_NOTFOUND != rc) { + php_error_docref0(NULL, E_WARNING, "%s", mdb_strerror(rc)); + } + return NULL; + } + + if(k.mv_data) { + if(newlen) *newlen = k.mv_size; + ret = estrndup(k.mv_data, k.mv_size); + } + + mdb_txn_reset(LMDB_IT(txn)); + + return ret; +} + +DBA_OPTIMIZE_FUNC(lmdb) +{ + return SUCCESS; +} + +DBA_SYNC_FUNC(lmdb) +{ + int rc; + + rc = mdb_env_sync(LMDB_IT(env), 1); + if (rc) { + php_error_docref0(NULL, E_WARNING, "%s", mdb_strerror(rc)); + return FAILURE; + } + + return SUCCESS; +} + +DBA_INFO_FUNC(lmdb) +{ + return estrdup(MDB_VERSION_STRING); +} + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/dba/php_lmdb.h b/ext/dba/php_lmdb.h new file mode 100644 index 0000000000..1b4928f49b --- /dev/null +++ b/ext/dba/php_lmdb.h @@ -0,0 +1,12 @@ +#ifndef PHP_LMDB_H +#define PHP_LMDB_H + +#if DBA_LMDB + +#include "php_dba.h" + +DBA_FUNCS(lmdb); + +#endif + +#endif diff --git a/ext/dba/tests/clean.inc b/ext/dba/tests/clean.inc index 7c53e7e61f..9f6d539a19 100644 --- a/ext/dba/tests/clean.inc +++ b/ext/dba/tests/clean.inc @@ -2,4 +2,5 @@ $db_filename = dirname(__FILE__) .'/test0.dbm'; // see test.inc @unlink($db_filename); @unlink($db_filename.'.lck'); + @unlink($db_filename.'-lock'); ?> diff --git a/ext/dba/tests/dba_lmdb.phpt b/ext/dba/tests/dba_lmdb.phpt new file mode 100644 index 0000000000..b32cd58bbd --- /dev/null +++ b/ext/dba/tests/dba_lmdb.phpt @@ -0,0 +1,38 @@ +--TEST-- +DBA LMDB handler test +--SKIPIF-- +<?php + $handler = 'lmdb'; + require_once dirname(__FILE__) .'/skipif.inc'; +?> +--FILE-- +<?php + $handler = 'lmdb'; + require_once dirname(__FILE__) .'/test.inc'; + $lock_flag = ''; // lock in library + require_once dirname(__FILE__) .'/dba_handler.inc'; +?> +===DONE=== +--CLEAN-- +<?php + require_once dirname(__FILE__) .'/clean.inc'; +?> +--EXPECTF-- +database handler: lmdb +3NYNYY +Content String 2 +Content 2 replaced +Read during write:%sallowed +"key number 6" written +Failed to write "key number 6" 2nd time +Content 2 replaced 2nd time +The 6th value +array(3) { + ["key number 6"]=> + string(13) "The 6th value" + ["key2"]=> + string(27) "Content 2 replaced 2nd time" + ["key5"]=> + string(23) "The last content string" +} +===DONE=== |