]> granicus.if.org Git - php/commitdiff
implement support for LMDB in ext/dba
authorAnatol Belski <ab@php.net>
Sun, 28 May 2017 16:13:26 +0000 (18:13 +0200)
committerAnatol Belski <ab@php.net>
Sun, 28 May 2017 16:33:12 +0000 (18:33 +0200)
don't abort txn if cursor is active

fix typos

ext/dba/config.m4
ext/dba/config.w32
ext/dba/dba.c
ext/dba/dba_lmdb.c [new file with mode: 0644]
ext/dba/php_lmdb.h [new file with mode: 0644]
ext/dba/tests/clean.inc
ext/dba/tests/dba_lmdb.phpt [new file with mode: 0644]

index 740cf14e39dc8b18ffd3da649dfc9dfe6ee735d7..a8bd561c50381b3a11c62b6d91f34609d3709afb 100644 (file)
@@ -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],[
index d521e6ad624ccbad3fec910f37ae308e77c11dfb..5eb542d986902b51a51558e17cb0caf816c327f8 100644 (file)
@@ -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");
+               }
+       }
 }
index 072f84b784a6fec7b4c581b730652d910ee399d7..a8470d9d59be55267947999bb2e99931cc49faa4 100644 (file)
@@ -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)
@@ -362,6 +363,9 @@ static dba_handler handler[] = {
 #endif
 #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 (file)
index 0000000..91f42e1
--- /dev/null
@@ -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 (file)
index 0000000..1b4928f
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef PHP_LMDB_H
+#define PHP_LMDB_H
+
+#if DBA_LMDB
+
+#include "php_dba.h"
+
+DBA_FUNCS(lmdb);
+
+#endif
+
+#endif
index 7c53e7e61f11bead45020a5ebc00590cbe18b4ca..9f6d539a195363993058e8d84447f7f51942d03a 100644 (file)
@@ -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 (file)
index 0000000..b32cd58
--- /dev/null
@@ -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===