From bfeb1690414995730a2feb90eaad407166327cb4 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov <kirr@nexedi.com> Date: Tue, 4 Apr 2017 13:13:09 +0300 Subject: [PATCH] Allow internal clients to specify intended access mode - read-only or read-write Most of our tools need only read access for working. However e.g. FileStorage, when opened in read-write mode, automatically creates database file and index. This way if database is opened in read-write mode a simple typo in path, e.g. to `zodb dump path` would lead to: - new database at path will be created - the dump will print nothing (empty database) - exit status will be 0 (ok) and no error will be reported. For this reason it is better tools declare access level they need so for read-only access request we can catch it with an error from storage. This, however, requires quite recent ZODB to work: https://github.com/zopefoundation/ZODB/pull/153 P.S. We don't want to force users to always specify read-only in URLs or zconf files because: - this is error prone - URL or zconf can be though as of file - when a program opens a file the program, not file, declares which type of access it wants. That's why access mode declaration has to be internal. --- setup.py | 2 +- zodbtools/util.py | 17 ++++++++++++++++- zodbtools/zodbcmp.py | 4 ++-- zodbtools/zodbdump.py | 2 +- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index a01adc2..f511100 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( keywords = 'zodb utility tool', packages = find_packages(), - install_requires = ['ZODB', 'zodburi'], + install_requires = ['ZODB', 'zodburi', 'six'], entry_points= {'console_scripts': ['zodb = zodbtools.zodb:main']}, diff --git a/zodbtools/util.py b/zodbtools/util.py index 9230fa7..28a04d4 100644 --- a/zodbtools/util.py +++ b/zodbtools/util.py @@ -18,6 +18,7 @@ import hashlib import zodburi +from six.moves.urllib_parse import urlsplit, urlunsplit def ashex(s): return s.encode('hex') @@ -76,11 +77,25 @@ def parse_tidrange(tidrange): # storageFromURL opens a ZODB-storage specified by url -def storageFromURL(url): +# read_only specifies read or read/write mode for requested access: +# - None: use default mode specified by url +# - True/False: explicitly request read-only / read-write mode +def storageFromURL(url, read_only=None): # no schema -> file:// if "://" not in url: url = "file://" + url + # read_only -> url + if read_only is not None: + scheme, netloc, path, query, fragment = urlsplit(url) + # XXX this won't have effect with zconfig:// but for file:// neo:// + # zeo:// etc ... it works + if scheme != "zconfig": + if len(query) > 0: + query += "&" + query += "read_only=%s" % read_only + url = urlunsplit((scheme, netloc, path, query, fragment)) + stor_factory, dbkw = zodburi.resolve_uri(url) stor = stor_factory() diff --git a/zodbtools/zodbcmp.py b/zodbtools/zodbcmp.py index b4a7728..94bb3e7 100644 --- a/zodbtools/zodbcmp.py +++ b/zodbtools/zodbcmp.py @@ -157,8 +157,8 @@ def main2(argv): print("E: invalid tidrange: %s" % e, file=sys.stderr) sys.exit(2) - stor1 = storageFromURL(storurl1) - stor2 = storageFromURL(storurl2) + stor1 = storageFromURL(storurl1, read_only=True) + stor2 = storageFromURL(storurl2, read_only=True) zcmp = storcmp(stor1, stor2, tidmin, tidmax, verbose) sys.exit(1 if zcmp else 0) diff --git a/zodbtools/zodbdump.py b/zodbtools/zodbdump.py index f1394c3..1f49787 100644 --- a/zodbtools/zodbdump.py +++ b/zodbtools/zodbdump.py @@ -116,6 +116,6 @@ def main(argv): print("E: invalid tidrange: %s" % e, file=sys.stderr) sys.exit(2) - stor = storageFromURL(storurl) + stor = storageFromURL(storurl, read_only=True) zodbdump(stor, tidmin, tidmax, hashonly) -- 2.30.9