implement support for LMDB in ext/dba

don't abort txn if cursor is active

fix typos
This commit is contained in:
Anatol Belski 2017-05-28 18:13:26 +02:00
parent 2058d8ed22
commit 16d7fd9d7f
7 changed files with 457 additions and 0 deletions

View 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],[

View 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");
}
}
}

View 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 */

353
ext/dba/dba_lmdb.c Normal file
View file

@ -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
*/

12
ext/dba/php_lmdb.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef PHP_LMDB_H
#define PHP_LMDB_H
#if DBA_LMDB
#include "php_dba.h"
DBA_FUNCS(lmdb);
#endif
#endif

View 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');
?>

View file

@ -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===