Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-lmdb for openSUSE:Factory checked in at 2021-04-21 20:59:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-lmdb (Old) and /work/SRC/openSUSE:Factory/.python-lmdb.new.12324 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-lmdb" Wed Apr 21 20:59:35 2021 rev:8 rq:886821 version:1.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-lmdb/python-lmdb.changes 2021-02-07 15:23:44.334179978 +0100 +++ /work/SRC/openSUSE:Factory/.python-lmdb.new.12324/python-lmdb.changes 2021-04-21 20:59:50.042242417 +0200 @@ -1,0 +2,21 @@ +Tue Apr 20 02:10:56 UTC 2021 - Mia Herkt <m...@0x0.st> + +- Update to v1.2.1 (no changes) + +------------------------------------------------------------------- +Sun Apr 18 21:58:00 UTC 2021 - Mia Herkt <m...@0x0.st> + +- Update to v1.2.0 + * Remove wheel generation for 2.7 because the manylinux images + no longer support it. + * Allow passing None as a value to transaction.del in CFFI + implementation for parity with cpython implementation. + * Fix Cursor.put behavior on a dupsort DB with append=True. + * Add warning to docs about use of Environment.set_mapsize. + This is currently an unresolved issue with upstream LMDB. + * CFFI implementation: + fix a seg fault when open_db returns map full. + * CFFI implementation: + fix a bug in open_db in a read-only environment. + +------------------------------------------------------------------- Old: ---- lmdb-1.1.0.tar.gz New: ---- lmdb-1.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-lmdb.spec ++++++ --- /var/tmp/diff_new_pack.5YHXiQ/_old 2021-04-21 20:59:50.558243229 +0200 +++ /var/tmp/diff_new_pack.5YHXiQ/_new 2021-04-21 20:59:50.562243235 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-lmdb -Version: 1.1.0 +Version: 1.2.1 Release: 0 Summary: Universal Python binding for the LMDB 'Lightning' Database License: OLDAP-2.8 ++++++ lmdb-1.1.0.tar.gz -> lmdb-1.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/ChangeLog new/lmdb-1.2.1/ChangeLog --- old/lmdb-1.1.0/ChangeLog 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/ChangeLog 2021-04-19 21:10:32.000000000 +0200 @@ -1,3 +1,32 @@ +2021-04-19 v1.2.1 +* Resolve CI bug where non-Linux wheels were not being published to PyPI. + +2021-04-15 v1.2.0 +* Update bundled LMDB to 0.9.29. + +* Add non-bundled testing to CI. + +* Remove wheel generation for 2.7 because the manylinux images no longer + support it. + +* Allow passing None as a value to transaction.del in CFFI implementation + for parity with cpython implementation. + +* Fix Cursor.put behavior on a dupsort DB with append=True. + +* Add warning to docs about use of Environment.set_mapsize. This is currently +an unresolved issue with upstream LMDB. + +* CFFI implementation: fix a seg fault when open_db returns map full. + +* CFFI implementation: fix a bug in open_db in a read-only environment. + + +2021-02-05 v1.1.1 +* Dowgrade underlying LMDB to 0.9.26. 0.9.27 has a minor defect that will + need to get resolved. + + 2021-02-04 v1.1.0 * Migrate CI pipeline from Travis and AppVeyor to Github Actions. Now includes comprehensive testing across 4 dimensions (OS, Python version, @@ -10,6 +39,7 @@ * Upgrade underlying LMDB to 0.9.27. + 2020-08-28 v1.0.0 * Start of new semantic versioning scheme. This would be a minor version bump from the 0.99 release if it were semantically versioned. @@ -19,6 +49,7 @@ underlying C library. By default, the patch will be applied unless this module is built with LMDB_PURE environment variable set. + 2020-08-13 v0.99 * Fix lmdb.tool encoding issues. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/PKG-INFO new/lmdb-1.2.1/PKG-INFO --- old/lmdb-1.1.0/PKG-INFO 2021-02-04 23:00:18.689847200 +0100 +++ new/lmdb-1.2.1/PKG-INFO 2021-04-19 21:10:55.101324800 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: lmdb -Version: 1.1.0 +Version: 1.2.1 Summary: Universal Python binding for the LMDB 'Lightning' Database Home-page: http://github.com/jnwatson/py-lmdb/ Author: David Wilson diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/docs/index.rst new/lmdb-1.2.1/docs/index.rst --- old/lmdb-1.1.0/docs/index.rst 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/docs/index.rst 2021-04-19 21:10:32.000000000 +0200 @@ -9,7 +9,7 @@ :maxdepth: 2 This is a universal Python binding for the `LMDB 'Lightning' Database -<http://symas.com/mdb/>`_. Two variants are provided and automatically selected +<http://lmdb.tech/>`_. Two variants are provided and automatically selected during install: a `CFFI <https://cffi.readthedocs.io/en/release-0.5/>`_ variant that supports `PyPy <http://www.pypy.org/>`_ and all versions of CPython >=2.7, and a C extension that supports CPython >=2.7 and >=3.4. Both variants @@ -105,7 +105,7 @@ Before getting in contact, please ensure you have thoroughly reviewed this documentation, and if applicable, the associated -`official Doxygen documentation <http://symas.com/mdb/doc/>`_. +`official Doxygen documentation <http://lmdb.tech/doc/>`_. If you have found a bug, please report it on the `GitHub issue tracker <https://github.com/dw/py-lmdb/issues>`_, or mail it to the list below if diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/lib/lmdb.h new/lmdb-1.2.1/lib/lmdb.h --- old/lmdb-1.1.0/lib/lmdb.h 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/lib/lmdb.h 2021-04-19 21:10:32.000000000 +0200 @@ -135,7 +135,7 @@ * * @author Howard Chu, Symas Corporation. * - * @copyright Copyright 2011-2020 Howard Chu, Symas Corp. All rights reserved. + * @copyright Copyright 2011-2021 Howard Chu, Symas Corp. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP @@ -200,7 +200,7 @@ /** Library minor version */ #define MDB_VERSION_MINOR 9 /** Library patch version */ -#define MDB_VERSION_PATCH 27 +#define MDB_VERSION_PATCH 29 /** Combine args a,b,c into a single integer for easy version comparisons */ #define MDB_VERINT(a,b,c) (((a) << 24) | ((b) << 16) | (c)) @@ -210,7 +210,7 @@ MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH) /** The release date of this library version */ -#define MDB_VERSION_DATE "October 26, 2020" +#define MDB_VERSION_DATE "March 16, 2021" /** A stringifier for the version info */ #define MDB_VERSTR(a,b,c,d) "LMDB " #a "." #b "." #c ": (" d ")" @@ -576,7 +576,7 @@ * <li>#MDB_NOTLS * Don't use Thread-Local Storage. Tie reader locktable slots to * #MDB_txn objects instead of to threads. I.e. #mdb_txn_reset() keeps - * the slot reseved for the #MDB_txn object. A thread may use parallel + * the slot reserved for the #MDB_txn object. A thread may use parallel * read-only transactions. A read-only transaction may span threads if * the user synchronizes its use. Applications that multiplex many * user threads over individual OS threads need this option. Such an @@ -928,7 +928,7 @@ typedef void MDB_assert_func(MDB_env *env, const char *msg); /** Set or reset the assert() callback of the environment. - * Disabled if liblmdb is buillt with NDEBUG. + * Disabled if liblmdb is built with NDEBUG. * @note This hack should become obsolete as lmdb's error handling matures. * @param[in] env An environment handle returned by #mdb_env_create(). * @param[in] func An #MDB_assert_func function, or 0. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/lib/mdb.c new/lmdb-1.2.1/lib/mdb.c --- old/lmdb-1.1.0/lib/mdb.c 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/lib/mdb.c 2021-04-19 21:10:32.000000000 +0200 @@ -5,7 +5,7 @@ * BerkeleyDB API, but much simplified. */ /* - * Copyright 2011-2020 Howard Chu, Symas Corp. + * Copyright 2011-2021 Howard Chu, Symas Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -493,7 +493,7 @@ * The string is printed literally, with no format processing. */ #define DPUTS(arg) DPRINTF(("%s", arg)) - /** Debuging output value of a cursor DBI: Negative in a sub-cursor. */ + /** Debugging output value of a cursor DBI: Negative in a sub-cursor. */ #define DDBI(mc) \ (((mc)->mc_flags & C_SUB) ? -(int)(mc)->mc_dbi : (int)(mc)->mc_dbi) /** @} */ @@ -6597,7 +6597,7 @@ dkey.mv_size = 0; - if (flags == MDB_CURRENT) { + if (flags & MDB_CURRENT) { if (!(mc->mc_flags & C_INITIALIZED)) return EINVAL; rc = MDB_SUCCESS; @@ -6992,7 +6992,7 @@ xdata.mv_size = 0; xdata.mv_data = ""; leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); - if (flags & MDB_CURRENT) { + if ((flags & (MDB_CURRENT|MDB_APPENDDUP)) == MDB_CURRENT) { xflags = MDB_CURRENT|MDB_NOSPILL; } else { mdb_xcursor_init1(mc, leaf); @@ -8481,7 +8481,6 @@ return rc; } - ki = mc->mc_ki[mc->mc_top]; mp = mc->mc_pg[mc->mc_top]; nkeys = NUMKEYS(mp); @@ -8493,19 +8492,18 @@ if (m3->mc_snum < mc->mc_snum) continue; if (m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] >= mc->mc_ki[mc->mc_top]) { /* if m3 points past last node in page, find next sibling */ - if (m3->mc_ki[mc->mc_top] >= nkeys) { - rc = mdb_cursor_sibling(m3, 1); - if (rc == MDB_NOTFOUND) { - m3->mc_flags |= C_EOF; - rc = MDB_SUCCESS; - continue; + if (m3->mc_ki[mc->mc_top] >= nkeys) { + rc = mdb_cursor_sibling(m3, 1); + if (rc == MDB_NOTFOUND) { + m3->mc_flags |= C_EOF; + rc = MDB_SUCCESS; + continue; + } + if (rc) + goto fail; } - if (rc) - goto fail; - } - if (m3->mc_ki[mc->mc_top] >= ki || - /* moved to right sibling */ m3->mc_pg[mc->mc_top] != mp) { if (m3->mc_xcursor && !(m3->mc_flags & C_EOF)) { MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]); /* If this node has dupdata, it may need to be reinited @@ -8527,10 +8525,10 @@ } m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; } - m3->mc_flags |= C_DEL; } } } + mc->mc_flags |= C_DEL; fail: if (rc) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/lib/py-lmdb/env-copy-txn.patch new/lmdb-1.2.1/lib/py-lmdb/env-copy-txn.patch --- old/lmdb-1.1.0/lib/py-lmdb/env-copy-txn.patch 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/lib/py-lmdb/env-copy-txn.patch 2021-04-19 21:10:32.000000000 +0200 @@ -1,5 +1,5 @@ diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h -index 361c93010..da6254819 100644 +index 69aa2751a2..ff7f77a10d 100644 --- a/libraries/liblmdb/lmdb.h +++ b/libraries/liblmdb/lmdb.h @@ -682,9 +682,14 @@ int mdb_env_copyfd(MDB_env *env, mdb_filehandle_t fd); @@ -32,10 +32,10 @@ /** @brief Return statistics about the LMDB environment. * diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c -index b70a6a54d..0b382a721 100644 +index 8cecdb2e69..9ede4183dd 100644 --- a/libraries/liblmdb/mdb.c +++ b/libraries/liblmdb/mdb.c -@@ -9334,12 +9334,12 @@ done: +@@ -9332,12 +9332,12 @@ done: /** Copy environment with compaction. */ static int ESECT @@ -50,7 +50,7 @@ pthread_t thr; pgno_t root, new_root; int rc = MDB_SUCCESS; -@@ -9385,9 +9385,11 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd) +@@ -9383,9 +9383,11 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd) if (rc) goto done; @@ -65,7 +65,7 @@ mp = (MDB_page *)my.mc_wbuf[0]; memset(mp, 0, NUM_METAS * env->me_psize); -@@ -9447,7 +9449,8 @@ finish: +@@ -9445,7 +9447,8 @@ finish: my.mc_error = rc; mdb_env_cthr_toggle(&my, 1 | MDB_EOF); rc = THREAD_FINISH(thr); @@ -75,7 +75,7 @@ done: #ifdef _WIN32 -@@ -9564,12 +9567,22 @@ leave: +@@ -9562,12 +9565,22 @@ leave: } int ESECT @@ -100,7 +100,7 @@ } int ESECT -@@ -9580,6 +9593,12 @@ mdb_env_copyfd(MDB_env *env, HANDLE fd) +@@ -9578,6 +9591,12 @@ mdb_env_copyfd(MDB_env *env, HANDLE fd) int ESECT mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags) @@ -113,7 +113,7 @@ { int rc; MDB_name fname; -@@ -9591,7 +9610,7 @@ mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags) +@@ -9589,7 +9608,7 @@ mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags) mdb_fname_destroy(fname); } if (rc == MDB_SUCCESS) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/lmdb/__init__.py new/lmdb-1.2.1/lmdb/__init__.py --- old/lmdb-1.1.0/lmdb/__init__.py 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/lmdb/__init__.py 2021-04-19 21:10:32.000000000 +0200 @@ -50,4 +50,4 @@ from lmdb.cffi import __all__ from lmdb.cffi import __doc__ -__version__ = '1.1.0' +__version__ = '1.2.1' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/lmdb/cffi.py new/lmdb-1.2.1/lmdb/cffi.py --- old/lmdb-1.1.0/lmdb/cffi.py 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/lmdb/cffi.py 2021-04-19 21:10:32.000000000 +0200 @@ -42,13 +42,13 @@ try: import __builtin__ except ImportError: - import builtins as __builtin__ + import builtins as __builtin__ # type: ignore import lmdb try: from lmdb import _config except ImportError: - _config = None + _config = None # type: ignore __all__ = [ @@ -362,7 +362,7 @@ 'libraries': [] } - _have_patched_lmdb = '-DHAVE_PATCHED_LMDB=1' in _config.CONFIG['extra_compile_args'] + _have_patched_lmdb = '-DHAVE_PATCHED_LMDB=1' in _config.CONFIG['extra_compile_args'] # type: ignore if _have_patched_lmdb: _CFFI_CDEF += _CFFI_CDEF_PATCHED @@ -370,13 +370,13 @@ _ffi = cffi.FFI() _ffi.cdef(_CFFI_CDEF) _lib = _ffi.verify(_CFFI_VERIFY, - modulename='lmdb_cffi', - ext_package='lmdb', - sources=_config_vars['extra_sources'], - extra_compile_args=_config_vars['extra_compile_args'], - include_dirs=_config_vars['extra_include_dirs'], - libraries=_config_vars['libraries'], - library_dirs=_config_vars['extra_library_dirs']) + modulename='lmdb_cffi', + ext_package='lmdb', + sources=_config_vars['extra_sources'], + extra_compile_args=_config_vars['extra_compile_args'], + include_dirs=_config_vars['extra_include_dirs'], + libraries=_config_vars['libraries'], + library_dirs=_config_vars['extra_library_dirs']) @_ffi.callback("int(char *, void *)") def _msg_func(s, _): @@ -535,8 +535,10 @@ """ def __nonzero__(self): return 0 + def __bool__(self): return False + def __repr__(self): return "<This used to be a LMDB resource but it was deleted or closed>" _invalid = Some_LMDB_Resource_That_Was_Deleted_Or_Closed() @@ -595,7 +597,7 @@ interpreter crash. Equivalent to `mdb_env_open() - <http://symas.com/mdb/doc/group__mdb.html#ga1fe2740e25b1689dc412e7b9faadba1b>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1fe2740e25b1689dc412e7b9faadba1b>`_ `path`: Location of directory (if `subdir=True`) or file prefix to store @@ -712,10 +714,10 @@ so that no readers may be active at all when a writer begins. """ def __init__(self, path, map_size=10485760, subdir=True, - readonly=False, metasync=True, sync=True, map_async=False, - mode=O_0755, create=True, readahead=True, writemap=False, - meminit=True, max_readers=126, max_dbs=0, max_spare_txns=1, - lock=True): + readonly=False, metasync=True, sync=True, map_async=False, + mode=O_0755, create=True, readahead=True, writemap=False, + meminit=True, max_readers=126, max_dbs=0, max_spare_txns=1, + lock=True): self._max_spare_txns = max_spare_txns self._spare_txns = [] @@ -726,6 +728,7 @@ raise _error("mdb_env_create", rc) self._env = envpp[0] self._deps = set() + self._creating_db_in_readonly = False self.set_mapsize(map_size) @@ -809,7 +812,18 @@ The new size in bytes. Equivalent to `mdb_env_set_mapsize() - <http://symas.com/mdb/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5>`_ + <http://lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5>`_ + + Warning: + There's a data race in the underlying library that may cause + catastrophic loss of data if you use this method. + + You are safe if one of the following are true: + * Only one process accessing a particular LMDB file ever calls + this method. + + * You use locking external to this library to ensure that only one + process accessing the current LMDB file can be inside this function. """ rc = _lib.mdb_env_set_mapsize(self._env, map_size) if rc: @@ -820,7 +834,7 @@ transactions. Repeat calls to :py:meth:`close` have no effect. Equivalent to `mdb_env_close() - <http://symas.com/mdb/doc/group__mdb.html#ga4366c43ada8874588b6a62fbda2d1e95>`_ + <http://lmdb.tech/doc/group__mdb.html#ga4366c43ada8874588b6a62fbda2d1e95>`_ """ if self._env: if self._deps: @@ -846,7 +860,7 @@ stored. Equivalent to `mdb_env_get_path() - <http://symas.com/mdb/doc/group__mdb.html#gac699fdd8c4f8013577cb933fb6a757fe>`_ + <http://lmdb.tech/doc/group__mdb.html#gac699fdd8c4f8013577cb933fb6a757fe>`_ """ path = _ffi.new('char **') rc = _lib.mdb_env_get_path(self._env, path) @@ -872,7 +886,7 @@ this parameter may be set only if compact=True. Equivalent to `mdb_env_copy2() or mdb_env_copy3() - <http://symas.com/mdb/doc/group__mdb.html#ga5d51d6130325f7353db0955dbedbc378>`_ + <http://lmdb.tech/doc/group__mdb.html#ga5d51d6130325f7353db0955dbedbc378>`_ """ flags = _lib.MDB_CP_COMPACT if compact else 0 if txn and not _have_patched_lmdb: @@ -908,7 +922,7 @@ is not available if the module was built with LMDB_PURE. Equivalent to `mdb_env_copyfd2() or mdb_env_copyfd3 - <http://symas.com/mdb/doc/group__mdb.html#ga5d51d6130325f7353db0955dbedbc378>`_ + <http://lmdb.tech/doc/group__mdb.html#ga5d51d6130325f7353db0955dbedbc378>`_ """ if txn and not _have_patched_lmdb: raise TypeError("Non-patched LMDB doesn't support transaction with env.copy") @@ -933,7 +947,7 @@ """Flush the data buffers to disk. Equivalent to `mdb_env_sync() - <http://symas.com/mdb/doc/group__mdb.html#ga85e61f05aa68b520cc6c3b981dba5037>`_ + <http://lmdb.tech/doc/group__mdb.html#ga85e61f05aa68b520cc6c3b981dba5037>`_ Data is always written to disk when :py:meth:`Transaction.commit` is called, but the operating system may keep it buffered. MDB always @@ -981,7 +995,7 @@ +--------------------+---------------------------------------+ Equivalent to `mdb_env_stat() - <http://symas.com/mdb/doc/group__mdb.html#gaf881dca452050efbd434cd16e4bae255>`_ + <http://lmdb.tech/doc/group__mdb.html#gaf881dca452050efbd434cd16e4bae255>`_ """ st = _ffi.new('MDB_stat *') rc = _lib.mdb_env_stat(self._env, st) @@ -1012,7 +1026,7 @@ +--------------------+---------------------------------------------+ Equivalent to `mdb_env_info() - <http://symas.com/mdb/doc/group__mdb.html#ga18769362c7e7d6cf91889a028a5c5947>`_ + <http://lmdb.tech/doc/group__mdb.html#ga18769362c7e7d6cf91889a028a5c5947>`_ """ info = _ffi.new('MDB_envinfo *') rc = _lib.mdb_env_info(self._env, info) @@ -1044,7 +1058,7 @@ 'readahead': not (flags & _lib.MDB_NORDAHEAD), 'writemap': bool(flags & _lib.MDB_WRITEMAP), 'meminit': not (flags & _lib.MDB_NOMEMINIT), - 'lock': not (flags & _lib.MDB_NOLOCK), + 'lock': not (flags & _lib.MDB_NOLOCK), } def max_key_size(self): @@ -1094,7 +1108,7 @@ same handle. As a special case, the main database is always open. Equivalent to `mdb_dbi_open() - <http://symas.com/mdb/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a>`_ + <http://lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a>`_ Named databases are implemented by *storing a special descriptor in the main database*. All databases in an environment *share the same file*. @@ -1199,9 +1213,13 @@ db = _Database(self, txn, key, reverse_key, dupsort, create, integerkey, integerdup, dupfixed) else: - with self.begin(write=not self.readonly) as txn: - db = _Database(self, txn, key, reverse_key, dupsort, create, - integerkey, integerdup, dupfixed) + try: + self._creating_db_in_readonly = True + with self.begin(write=not self.readonly) as txn: + db = _Database(self, txn, key, reverse_key, dupsort, create, + integerkey, integerdup, dupfixed) + finally: + self._creating_db_in_readonly = False self._dbs[key] = db return db @@ -1291,7 +1309,7 @@ txn.put('a', 'b') Equivalent to `mdb_txn_begin() - <http://symas.com/mdb/doc/group__mdb.html#gad7ea55da06b77513609efebd44b26920>`_ + <http://lmdb.tech/doc/group__mdb.html#gad7ea55da06b77513609efebd44b26920>`_ `env`: Environment the transaction should be on. @@ -1362,11 +1380,17 @@ self._write = True else: try: # Exception catch in order to avoid racy 'if txns:' test + if env._creating_db_in_readonly: # Don't use spare txns for creating a DB when read-only + raise IndexError self._txn = env._spare_txns.pop() env._max_spare_txns += 1 rc = _lib.mdb_txn_renew(self._txn) if rc: + while self._deps: + self._deps.pop()._invalidate() _lib.mdb_txn_abort(self._txn) + self._txn = _invalid + self._invalidate() raise _error("mdb_txn_renew", rc) except IndexError: txnpp = _ffi.new('MDB_txn **') @@ -1424,7 +1448,7 @@ unavailable, and invalidates existing cursors. Equivalent to `mdb_drop() - <http://symas.com/mdb/doc/group__mdb.html#gab966fab3840fc54a6571dfb32b00f2db>`_ + <http://lmdb.tech/doc/group__mdb.html#gab966fab3840fc54a6571dfb32b00f2db>`_ """ while db._deps: db._deps.pop()._invalidate() @@ -1449,11 +1473,13 @@ self._invalidate() return True + return False + def commit(self): """Commit the pending transaction. Equivalent to `mdb_txn_commit() - <http://symas.com/mdb/doc/group__mdb.html#ga846fbd6f46105617ac9f4d76476f6597>`_ + <http://lmdb.tech/doc/group__mdb.html#ga846fbd6f46105617ac9f4d76476f6597>`_ """ while self._deps: self._deps.pop()._invalidate() @@ -1471,7 +1497,7 @@ been closed. Equivalent to `mdb_txn_abort() - <http://symas.com/mdb/doc/group__mdb.html#ga73a5938ae4c3239ee11efa07eb22b882>`_ + <http://lmdb.tech/doc/group__mdb.html#ga73a5938ae4c3239ee11efa07eb22b882>`_ """ if self._txn: while self._deps: @@ -1489,7 +1515,7 @@ a `dupsort=True` database. Equivalent to `mdb_get() - <http://symas.com/mdb/doc/group__mdb.html#ga8bf10cd91d3f3a83a34d04ce6b07992d>`_ + <http://lmdb.tech/doc/group__mdb.html#ga8bf10cd91d3f3a83a34d04ce6b07992d>`_ """ rc = _lib.pymdb_get(self._txn, (db or self._db)._dbi, key, len(key), self._val) @@ -1508,7 +1534,7 @@ On success, the cursor is positioned on the new record. Equivalent to `mdb_put() - <http://symas.com/mdb/doc/group__mdb.html#ga4fa8573d9236d54687c61827ebf8cac0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga4fa8573d9236d54687c61827ebf8cac0>`_ `key`: Bytestring key to store. @@ -1576,7 +1602,7 @@ """Delete a key from the database. Equivalent to `mdb_del() - <http://symas.com/mdb/doc/group__mdb.html#gab8182f9360ea69ac0afd4a4eaab1ddb0>`_ + <http://lmdb.tech/doc/group__mdb.html#gab8182f9360ea69ac0afd4a4eaab1ddb0>`_ `key`: The key to delete. @@ -1588,6 +1614,9 @@ Returns True if at least one key was deleted. """ + if value is None: # for bug-compatibility with cpython impl + value = EMPTY_BYTES + rc = _lib.pymdb_del(self._txn, (db or self._db)._dbi, key, len(key), value, len(value)) self._mutations += 1 @@ -1607,7 +1636,7 @@ Structure for navigating a database. Equivalent to `mdb_cursor_open() - <http://symas.com/mdb/doc/group__mdb.html#ga9ff5d7bd42557fd5ee235dc1d62613aa>`_ + <http://lmdb.tech/doc/group__mdb.html#ga9ff5d7bd42557fd5ee235dc1d62613aa>`_ `db`: :py:class:`_Database` to navigate. @@ -1905,9 +1934,9 @@ duplicates, the cursor is positioned on the first value ("duplicate"). Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_FIRST - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_FIRST) @@ -1918,9 +1947,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_FIRST_DUP - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_FIRST_DUP) @@ -1932,9 +1961,9 @@ duplicates, the cursor is positioned on the last value ("duplicate"). Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_LAST - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_LAST) @@ -1945,9 +1974,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_LAST_DUP - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_LAST_DUP) @@ -1960,9 +1989,9 @@ to the previous key. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_PREV - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_PREV) @@ -1974,9 +2003,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_PREV_DUP - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_PREV_DUP) @@ -1987,9 +2016,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_PREV_NODUP - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_PREV_NODUP) @@ -2002,9 +2031,9 @@ first value of the next key. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_NEXT - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_NEXT) @@ -2015,9 +2044,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_NEXT_DUP - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_NEXT_DUP) @@ -2028,9 +2057,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_NEXT_NODUP - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get(_lib.MDB_NEXT_NODUP) @@ -2043,9 +2072,9 @@ ("duplicate") for the key. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_SET_KEY - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get_kv(_lib.MDB_SET_KEY, key, EMPTY_BYTES) @@ -2057,9 +2086,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_GET_BOTH - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ return self._cursor_get_kv(_lib.MDB_GET_BOTH, key, value) @@ -2118,7 +2147,7 @@ next_op = _lib.MDB_NEXT_DUP a = bytearray() - l = list() + lst = list() for key in keys: if self.set_key(key): while self._valid: @@ -2129,16 +2158,16 @@ if dupfixed_bytes: gen = ( - (key, val[i:i+dupfixed_bytes]) + (key, val[i:i + dupfixed_bytes]) for i in range(0, len(val), dupfixed_bytes)) if keyfixed: for k, v in gen: a.extend(k + v) else: for k, v in gen: - l.append((k, v)) + lst.append((k, v)) else: - l.append((key, val)) + lst.append((key, val)) if dupdata: self._cursor_get(next_op) @@ -2148,7 +2177,7 @@ if keyfixed: return memoryview(a) else: - return l + return lst def set_range(self, key): """Seek to the first key greater than or equal to `key`, returning @@ -2160,9 +2189,9 @@ ("duplicate") for the key. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_SET_RANGE - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ if not key: return self.first() @@ -2176,9 +2205,9 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_get() - <http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ + <http://lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0>`_ with `MDB_GET_BOTH_RANGE - <http://symas.com/mdb/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127>`_ """ rc = self._cursor_get_kv(_lib.MDB_GET_BOTH_RANGE, key, value) # issue #126: MDB_GET_BOTH_RANGE does not satisfy its documentation, @@ -2196,7 +2225,7 @@ meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_del() - <http://symas.com/mdb/doc/group__mdb.html#ga26a52d3efcfd72e5bf6bd6960bf75f95>`_ + <http://lmdb.tech/doc/group__mdb.html#ga26a52d3efcfd72e5bf6bd6960bf75f95>`_ """ v = self._valid if v: @@ -2215,7 +2244,7 @@ Only meaningful for databases opened with `dupsort=True`. Equivalent to `mdb_cursor_count() - <http://symas.com/mdb/doc/group__mdb.html#ga4041fd1e1862c6b7d5f10590b86ffbe2>`_ + <http://lmdb.tech/doc/group__mdb.html#ga4041fd1e1862c6b7d5f10590b86ffbe2>`_ """ countp = _ffi.new('size_t *') rc = _lib.mdb_cursor_count(self._cur, countp) @@ -2229,7 +2258,7 @@ success, the cursor is positioned on the key. Equivalent to `mdb_cursor_put() - <http://symas.com/mdb/doc/group__mdb.html#ga1f83ccb40011837ff37cc32be01ad91e>`_ + <http://lmdb.tech/doc/group__mdb.html#ga1f83ccb40011837ff37cc32be01ad91e>`_ `key`: Bytestring key to store. @@ -2260,7 +2289,10 @@ if not overwrite: flags |= _lib.MDB_NOOVERWRITE if append: - flags |= _lib.MDB_APPEND + if self.txn._db._flags & _lib.MDB_DUPSORT: + flags |= _lib.MDB_APPENDDUP + else: + flags |= _lib.MDB_APPEND rc = _lib.pymdb_cursor_put(self._cur, key, len(key), val, len(val), flags) self.txn._mutations += 1 @@ -2325,7 +2357,7 @@ else: raise _error("mdb_cursor_put", rc) self._cursor_get(_lib.MDB_GET_CURRENT) - return added, added-skipped + return added, added - skipped def replace(self, key, val): """Store a record, returning its previous value if one existed. Returns diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/lmdb/cpython.c new/lmdb-1.2.1/lmdb/cpython.c --- old/lmdb-1.1.0/lmdb/cpython.c 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/lmdb/cpython.c 2021-04-19 21:10:32.000000000 +0200 @@ -2586,7 +2586,7 @@ flags |= MDB_NOOVERWRITE; } if(arg.append) { - flags |= MDB_APPEND; + flags |= (self->trans->db->flags & MDB_DUPSORT) ? MDB_APPENDDUP : MDB_APPEND; } UNLOCKED(rc, mdb_cursor_put(self->curs, &arg.key, &arg.val, flags)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/lmdb.egg-info/PKG-INFO new/lmdb-1.2.1/lmdb.egg-info/PKG-INFO --- old/lmdb-1.1.0/lmdb.egg-info/PKG-INFO 2021-02-04 23:00:14.000000000 +0100 +++ new/lmdb-1.2.1/lmdb.egg-info/PKG-INFO 2021-04-19 21:10:48.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: lmdb -Version: 1.1.0 +Version: 1.2.1 Summary: Universal Python binding for the LMDB 'Lightning' Database Home-page: http://github.com/jnwatson/py-lmdb/ Author: David Wilson diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/tests/crash_test.py new/lmdb-1.2.1/tests/crash_test.py --- old/lmdb-1.1.0/tests/crash_test.py 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/tests/crash_test.py 2021-04-19 21:10:32.000000000 +0200 @@ -1,5 +1,5 @@ # -# Copyright 2013 The py-lmdb authors, all rights reserved. +# Copyright 2013-2021 The py-lmdb authors, all rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted only as authorized by the OpenLDAP @@ -32,9 +32,11 @@ from __future__ import absolute_import from __future__ import with_statement +import sys import itertools import random import unittest +import multiprocessing import lmdb import testlib @@ -278,5 +280,40 @@ txn2 = env.begin(write=False) self.assertRaises(lmdb.InvalidParameterError, txn2.cursor, db=db) +if sys.version_info[:2] >= (3, 4): + class MapResizeTest(unittest.TestCase): + def tearDown(self): + testlib.cleanup() + + @staticmethod + def do_resize(path): + ''' + Increase map size and fill up database, making sure that the root page is no longer + accessible in the main process. + ''' + data = [i.to_bytes(4, 'little') for i in range(400)] + with lmdb.open(path, max_dbs=10, create=False, map_size=32000) as env: + env.open_db(b'foo') + env.set_mapsize(64000) + with env.begin(write=True) as txn: + for datum in data: + txn.put(datum, b'0') + + def test_opendb_resize(self): + ''' + Test that we correctly handle a MDB_MAP_RESIZED in env.open_db. + + Would seg fault in cffi implementation + ''' + mpctx = multiprocessing.get_context('spawn') + path, env = testlib.temp_env(max_dbs=10, map_size=32000) + env.close() + env = lmdb.open(path, max_dbs=10, map_size=32000, readonly=True) + proc = mpctx.Process(target=self.do_resize, args=(path,)) + proc.start() + proc.join(5) + assert proc.exitcode is not None + self.assertRaises(lmdb.MapResizedError, env.open_db, b'foo') + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/tests/cursor_test.py new/lmdb-1.2.1/tests/cursor_test.py --- old/lmdb-1.1.0/tests/cursor_test.py 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/tests/cursor_test.py 2021-04-19 21:10:32.000000000 +0200 @@ -1,5 +1,5 @@ # -# Copyright 2013 The py-lmdb authors, all rights reserved. +# Copyright 2013-2021 The py-lmdb authors, all rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted only as authorized by the OpenLDAP @@ -27,6 +27,8 @@ import sys import unittest +import lmdb + import testlib from testlib import B from testlib import BT @@ -140,6 +142,25 @@ def testPut(self): pass +class CursorTest2(unittest.TestCase): + def tearDown(self): + testlib.cleanup() + + def setUp(self): + self.path, self.env = testlib.temp_env() + self.db = self.env.open_db(b'foo', dupsort=True) + self.txn = self.env.begin(write=True, db=self.db) + self.c = self.txn.cursor() + + def testIterWithDeletes(self): + ''' A problem identified in LMDB 0.9.27 ''' + self.c.put(b'\x00\x01', b'hehe', dupdata=True) + self.c.put(b'\x00\x02', b'haha', dupdata=True) + self.c.set_key(b'\x00\x02') + it = self.c.iternext() + self.assertEqual((b'\x00\x02', b'haha'), next(it)) + self.txn.delete(b'\x00\x01', b'hehe', db=self.db) + self.assertRaises(StopIteration, next, it) class PutmultiTest(CursorTestBase): def test_empty_seq(self): @@ -171,7 +192,7 @@ def test_bad_seq1(self): self.assertRaises(Exception, - lambda: self.c.putmulti(range(2))) + lambda: self.c.putmulti(range(2))) def test_dupsort(self): _, env = testlib.temp_env() @@ -181,7 +202,7 @@ tups = [BT('a', 'value1'), BT('b', 'value1'), BT('b', 'value2')] assert (3, 3) == c.putmulti(tups) - def test_dupsort_append(self): + def test_dupsort_putmulti_append(self): _, env = testlib.temp_env() db1 = env.open_db(B('db1'), dupsort=True) txn = env.begin(write=True, db=db1) @@ -189,6 +210,15 @@ tups = [BT('a', 'value1'), BT('b', 'value1'), BT('b', 'value2')] assert (3, 3) == c.putmulti(tups, append=True) + def test_dupsort_put_append(self): + _, env = testlib.temp_env() + db1 = env.open_db(B('db1'), dupsort=True) + txn = env.begin(write=True, db=db1) + with txn.cursor() as c: + assert c.put(B('a'), B('value1'), append=True) + assert c.put(B('b'), B('value1'), append=True) + assert c.put(B('b'), B('value2'), append=True) + class ReplaceTest(CursorTestBase): def test_replace(self): assert None is self.c.replace(B('a'), B('')) @@ -280,5 +310,22 @@ # Getting the value does prefault the data, even if we only get it by pointer assert minflts_after_value - minflts_after_key > 1000 +class CursorReadOnlyTest(unittest.TestCase): + def tearDown(self): + testlib.cleanup() + + def test_cursor_readonly(self): + ''' + Tests whether you can open a cursor on a sub-db at all in a read-only environment. + ''' + path, env = testlib.temp_env(max_dbs=10) + env.open_db(b'foo') + env.close() + with lmdb.open(path, max_dbs=10, readonly=True) as env: + db2 = env.open_db(b'foo') + with env.begin(db=db2) as txn: + with txn.cursor(db=db2): + pass + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lmdb-1.1.0/tests/txn_test.py new/lmdb-1.2.1/tests/txn_test.py --- old/lmdb-1.1.0/tests/txn_test.py 2021-02-04 23:00:08.000000000 +0100 +++ new/lmdb-1.2.1/tests/txn_test.py 2021-04-19 21:10:32.000000000 +0200 @@ -520,13 +520,24 @@ _, env = testlib.temp_env() db = env.open_db(B('db1'), dupsort=True) txn = env.begin(write=True, db=db) - assert None == txn.replace(B('a'), B('x')) + assert None is txn.replace(B('a'), B('x')) assert B('x') == txn.replace(B('a'), B('y')) assert B('y') == txn.replace(B('a'), B('z')) cur = txn.cursor() assert cur.set_key(B('a')) assert [B('z')] == list(cur.iternext_dup()) + def test_dupsort_del_none(self): + _, env = testlib.temp_env() + db = env.open_db(B('db1'), dupsort=True) + with env.begin(write=True, db=db) as txn: + assert txn.put(B('a'), B('a')) + assert txn.put(B('a'), B('b')) + cur = txn.cursor() + assert cur.set_key(B('a')) + assert [B('a'), B('b')] == list(cur.iternext_dup()) + assert txn.delete(B('a'), None) + def test_dupdata_no_dupsort(self): _, env = testlib.temp_env() txn = env.begin(write=True)