Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-apsw for openSUSE:Factory checked in at 2023-12-03 20:49:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-apsw (Old) and /work/SRC/openSUSE:Factory/.python-apsw.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-apsw" Sun Dec 3 20:49:15 2023 rev:20 rq:1130519 version:3.44.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-apsw/python-apsw.changes 2023-11-15 21:09:48.422952504 +0100 +++ /work/SRC/openSUSE:Factory/.python-apsw.new.25432/python-apsw.changes 2023-12-03 20:49:36.723536490 +0100 @@ -1,0 +2,15 @@ +Sat Dec 2 20:18:36 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 3.44.2.0: + * Added `logger` parameter to :func:`apsw.ext.log_sqlite` to + use a specific :class:`logging.Logger` (:issue:`493`) + * Added :func:`apsw.ext.result_string` to turn an result code + into a string, taking into account if it is extended or not. + * Provide detail when C implemented objects are printed. For + example :class:`connections <Connection>` include the filename. + * Added :meth:`URIFilename.parameters` (:issue:`496`) + * :class:`URIFilename` are only valid for the duration of the + :meth:`VFS.xOpen` call. If you save and use the object later + you will get an exception. (:issue:`501`) + +------------------------------------------------------------------- Old: ---- apsw-3.44.0.0.tar.gz New: ---- apsw-3.44.2.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-apsw.spec ++++++ --- /var/tmp/diff_new_pack.OHZpkc/_old 2023-12-03 20:49:37.231555151 +0100 +++ /var/tmp/diff_new_pack.OHZpkc/_new 2023-12-03 20:49:37.231555151 +0100 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-apsw -Version: 3.44.0.0 +Version: 3.44.2.0 Release: 0 Summary: Another Python SQLite Wrapper License: Zlib ++++++ apsw-3.44.0.0.tar.gz -> apsw-3.44.2.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/Makefile new/apsw-3.44.2.0/Makefile --- old/apsw-3.44.0.0/Makefile 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/Makefile 2023-11-29 17:45:17.000000000 +0100 @@ -1,8 +1,8 @@ -SQLITEVERSION=3.44.0 +SQLITEVERSION=3.44.2 APSWSUFFIX=.0 -RELEASEDATE="6 November 2023" +RELEASEDATE="30 November 2023" VERSION=$(SQLITEVERSION)$(APSWSUFFIX) VERDIR=apsw-$(VERSION) @@ -76,7 +76,7 @@ dev-depends: ## pip installs packages useful for development (none are necessary except setuptools) $(PYTHON) -m pip install -U --upgrade-strategy eager build wheel setuptools pip - $(PYTHON) -m pip install -U --upgrade-strategy eager yapf mypy pdbpp coverage flake8 + $(PYTHON) -m pip install -U --upgrade-strategy eager mypy pdbpp coverage flake8 ruff # This is probably gnu make specific but only developers use this makefile $(GENDOCS): doc/%.rst: src/%.c tools/code2rst.py @@ -95,6 +95,7 @@ build_ext: src/apswversion.h apsw/__init__.pyi src/apsw.docstrings ## Fetches SQLite and builds the extension env $(PYTHON) setup.py fetch --version=$(SQLITEVERSION) --all build_ext -DSQLITE_ENABLE_COLUMN_METADATA --inplace --force --enable-all-extensions + env $(PYTHON) setup.py build_test_extension src/faultinject.h: tools/genfaultinject.py -rm src/faultinject.h diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/apsw/__init__.pyi new/apsw-3.44.2.0/apsw/__init__.pyi --- old/apsw-3.44.0.0/apsw/__init__.pyi 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/apsw/__init__.pyi 2023-11-29 17:45:17.000000000 +0100 @@ -1453,7 +1453,9 @@ def pragma(self, name: str, value: Optional[SQLiteValue] = None) -> Any: """Issues the pragma (with the value if supplied) and returns the result with :attr:`the least amount of structure <Cursor.get>`. For example - :code:`pragma("user_version")` will return just the number. + :code:`pragma("user_version")` will return just the number, while + :code:`pragma("journal_mode", "WAL")` will return the journal mode + now in effect. Pragmas do not support bindings, so this method is a convenient alternative to composing SQL text. @@ -2220,11 +2222,18 @@ or the main database flag is set. You can safely pass it on to the :class:`VFSFile` constructor - which knows how to get the name back out.""" + which knows how to get the name back out. The URIFilename is + only valid for the duration of the xOpen call. If you save + and use the object later you will get an exception.""" def filename(self) -> str: """Returns the filename.""" ... + parameters: tuple[str, ...] + """A tuple of the parameter names present. + + Calls: `sqlite3_uri_key <https://sqlite.org/c3ref/uri_boolean.html>`__""" + def uri_boolean(self, name: str, default: bool) -> bool: """Returns the boolean value for parameter `name` or `default` if not present. @@ -2281,7 +2290,7 @@ `PyErr_Display`.""" ... - def __init__(self, vfs: str, filename: str | URIFilename, flags: list[int, int]): + def __init__(self, vfs: str, filename: str | URIFilename, flags: list[int]): """:param vfs: The vfs you want to inherit behaviour from. You can use an empty string ``""`` to inherit from the default vfs. :param name: The name of the file being opened. May be an instance of :class:`URIFilename`. @@ -3787,6 +3796,8 @@ """For `Authorizer Action Codes <https://sqlite.org/c3ref/c_alter_table.html>'__""" SQLITE_REPLACE: int = 5 """For `Conflict resolution modes <https://sqlite.org/c3ref/c_fail.html>'__""" +SQLITE_RESULT_SUBTYPE: int = 16777216 +"""For `Function Flags <https://sqlite.org/c3ref/c_deterministic.html>'__""" SQLITE_ROLLBACK: int = 1 """For `Conflict resolution modes <https://sqlite.org/c3ref/c_fail.html>'__""" SQLITE_ROW: int = 100 @@ -4052,7 +4063,8 @@ """Function Flags mapping names to int and int to names. Doc at https://sqlite.org/c3ref/c_deterministic.html -SQLITE_DETERMINISTIC SQLITE_DIRECTONLY SQLITE_INNOCUOUS SQLITE_SUBTYPE""" +SQLITE_DETERMINISTIC SQLITE_DIRECTONLY SQLITE_INNOCUOUS +SQLITE_RESULT_SUBTYPE SQLITE_SUBTYPE""" mapping_limits: dict[str | int, int | str] """Run-Time Limit Categories mapping names to int and int to names. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/apsw/bestpractice.py new/apsw-3.44.2.0/apsw/bestpractice.py --- old/apsw-3.44.0.0/apsw/bestpractice.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/apsw/bestpractice.py 2023-11-29 17:45:17.000000000 +0100 @@ -1,7 +1,6 @@ # -""" -Module to ensure SQLite usage prevents common mistakes, and best performance. -""" + +"""Ensure SQLite usage prevents common mistakes, and get best performance.""" from __future__ import annotations diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/apsw/ext.py new/apsw-3.44.2.0/apsw/ext.py --- old/apsw-3.44.0.0/apsw/ext.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/apsw/ext.py 2023-11-29 17:45:17.000000000 +0100 @@ -41,6 +41,14 @@ return s in _keywords +def result_string(code: int) -> str: + """Turns a result or extended result code into a string. + The appropriate mapping based on the value is used.""" + if code < 256: + return apsw.mapping_result_codes.get(code, str(code)) # type: ignore + return apsw.mapping_extended_result_codes.get(code, str(code)) # type: ignore + + class DataClassRowFactory: """Returns each row as a :mod:`dataclass <dataclasses>`, accessible by column name. @@ -281,24 +289,24 @@ explain=explain) -def log_sqlite(*, level: int = logging.ERROR) -> None: +def log_sqlite(*, level: int = logging.ERROR, logger: logging.Logger | None = None) -> None: """Send SQLite `log messages <https://www.sqlite.org/errlog.html>`__ to :mod:`logging` :param level: level to log at + :param logger: Use the specific logger """ def handler(errcode: int, message: str) -> None: nonlocal level - err_str = apsw.mapping_result_codes.get(errcode & 255, str(errcode)) + err_str = result_string(errcode) extra = {"sqlite_code": errcode, "sqlite_code_name": err_str, "sqlite_message": message} if errcode & 0xff == apsw.SQLITE_WARNING: level = min(level, logging.WARNING) - logging.log(level, - "SQLITE_LOG: %s (%d) %s %s", + (logger or logging).log(level, + "SQLITE_LOG: %s (%d) %s", message, errcode, err_str, - apsw.mapping_extended_result_codes.get(errcode, ""), extra=extra) apsw.config(apsw.SQLITE_CONFIG_LOG, handler) @@ -1198,7 +1206,7 @@ return v # type: ignore[no-any-return] def _Column_repr_invalid(self, which: int) -> apsw.SQLiteValue: - v = self._Column_get(which) # type: ignore[attr-defined] + v = self._Column_get(which) # type: ignore[attr-defined] return v if v is None or isinstance(v, (int, float, str, bytes)) else repr(v) def _Column_By_Attr(self, which: int) -> apsw.SQLiteValue: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/apsw/shell.py new/apsw-3.44.2.0/apsw/shell.py --- old/apsw-3.44.0.0/apsw/shell.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/apsw/shell.py 2023-11-29 17:45:17.000000000 +0100 @@ -2098,7 +2098,7 @@ def log_handler(self, code, message): "Called with SQLite log messages when logging is ON" - code = f"( { code } - { apsw.mapping_result_codes.get(code, 'unknown') } ) " + code = f"( { code } - { apsw.ext.result_string(code) } ) " self.write_error(code + message + "\n") def command_log(self, cmd): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/apsw/tests.py new/apsw-3.44.2.0/apsw/tests.py --- old/apsw-3.44.0.0/apsw/tests.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/apsw/tests.py 2023-11-29 17:45:17.000000000 +0100 @@ -3460,8 +3460,6 @@ check("I", "en_us", equal=True) def testJSON1Extension(self): - if not self.checkOptionalExtension("json1", "select json('{}')"): - return # some sanity checks that it is working l = self.db.cursor().execute("select json_array_length('[1,2,3,4]')").fetchall()[0][0] self.assertEqual(l, 4) @@ -5374,14 +5372,13 @@ f"file { filename } function { name } calls PyGILState_Ensure but does not have MakeExistingException" ) # not further checked - if name.split("_")[0] in ("ZeroBlobBind", "APSWVFS", "APSWVFSFile", "APSWBuffer", "FunctionCBInfo", - "apswurifilename"): + if name.split("_")[0] in ("ZeroBlobBind", "APSWVFS", "APSWVFSFile", "APSWBuffer", "FunctionCBInfo"): return checks = { "APSWCursor": { "skip": ("dealloc", "init", "dobinding", "dobindings", "do_exec_trace", "do_row_trace", "step", "close", - "close_internal", "tp_traverse"), + "close_internal", "tp_traverse", "tp_str"), "req": { "use": "CHECK_USE", "closed": "CHECK_CURSOR_CLOSED", @@ -5391,7 +5388,7 @@ "Connection": { "skip": ("internal_cleanup", "dealloc", "init", "close", "interrupt", "close_internal", "remove_dependent", "readonly", "getmainfilename", "db_filename", "traverse", "clear", - "tp_traverse", "get_cursor_factory", "set_cursor_factory"), + "tp_traverse", "get_cursor_factory", "set_cursor_factory", "tp_str"), "req": { "use": "CHECK_USE", "closed": "CHECK_CLOSED", @@ -5399,7 +5396,7 @@ "order": ("use", "closed") }, "APSWBlob": { - "skip": ("dealloc", "init", "close", "close_internal"), + "skip": ("dealloc", "init", "close", "close_internal", "tp_str"), "req": { "use": "CHECK_USE", "closed": "CHECK_BLOB_CLOSED" @@ -5407,7 +5404,7 @@ "order": ("use", "closed") }, "APSWBackup": { - "skip": ("dealloc", "init", "close_internal", "get_remaining", "get_page_count"), + "skip": ("dealloc", "init", "close_internal", "get_remaining", "get_page_count", "tp_str"), "req": { "use": "CHECK_USE", "closed": "CHECK_BACKUP_CLOSED" @@ -5457,6 +5454,11 @@ "apswfcntl": { "req": {} }, + "apswurifilename": { + "req": { + "check": "CHECK_SCOPE" + } + } } prefix, base = name.split("_", 1) @@ -6190,6 +6192,8 @@ # assertEqual(name.uri_boolean("foo", False), False) assertEqual(name.uri_boolean("bam", False), True) assertEqual(name.uri_boolean("baz", True), False) + assertRaises(AttributeError, setattr, name, "parameters", 3) + assertEqual(name.parameters, ('foo', 'bar', 'bam', 'baz')) 1 / 0 testvfs = TVFS() @@ -10376,6 +10380,85 @@ else: self.assertEqual(res, r) + def testTpStr(self): + "Helpful descriptions of str(apsw objects)" + self.db.execute("create table x(y); insert into x values(x'abcdef1012')") + blob =self.db.blob_open("main", "x", "y", self.db.last_insert_rowid(), 0) + blob2 =self.db.blob_open("main", "x", "y", self.db.last_insert_rowid(), 0) + blob2.close() + db2=apsw.Connection("") + cur2=db2.cursor() + cur2.close() + db3=apsw.Connection("") + cur3=db3.cursor() + db3.close() + backup2=db2.backup("main", self.db, "main") + backup2.close() + name_catch=[] + class TVFS(apsw.VFS): + + def __init__(self): + apsw.VFS.__init__(self, "uritest", "") + + def xOpen(self, name, flags): + assert isinstance(name, apsw.URIFilename) + name_catch.append((name, str(name))) + raise apsw.SQLError() + + # MacOS fails the name we provide returning + # cantopen for this, so we avoid their code + def xFullPathname(self, name: str) -> str: + return name + + t = TVFS() + + with contextlib.suppress(apsw.SQLError): + with tempfile.NamedTemporaryFile() as n: + apsw.Connection(n.name, vfs="uritest") + + self.assertEqual(len(name_catch), 1) + uriname, urinamestr = name_catch[0] + + objects = (self.db, + db2, + db3, + self.db.cursor(), + cur2, + cur3, + apsw.zeroblob(3), + blob, + blob2, + uriname, + apsw.VFS("aname", ""), + apsw.VFSFile("", self.db.db_filename("main"), + [apsw.SQLITE_OPEN_MAIN_DB | apsw.SQLITE_OPEN_CREATE | apsw.SQLITE_OPEN_READWRITE, 0]), + db2.backup("main", self.db, "main"), + backup2, + ) + for o in objects: + self.assertNotEqual(repr(o), str(o)) + # issue 501 + if isinstance(o, apsw.URIFilename): + self.assertNotEqual(repr(o), urinamestr) + self.assertNotEqual(str(o), urinamestr) + + # more issue 501 + with contextlib.suppress(ValueError): + uriname.filename() + 1/0 + with contextlib.suppress(ValueError): + uriname.parameters + 1/0 + with contextlib.suppress(ValueError): + uriname.uri_boolean("name", False) + 1/0 + with contextlib.suppress(ValueError): + uriname.uri_int("name", 0) + 1/0 + with contextlib.suppress(ValueError): + uriname.uri_parameter("name") + 1/0 + # This test is run last by deliberate name choice. If it did # uncover any bugs there isn't much that can be done to turn the # checker off. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/checksums new/apsw-3.44.2.0/checksums --- old/apsw-3.44.0.0/checksums 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/checksums 2023-11-29 17:45:17.000000000 +0100 @@ -1,23 +1,23 @@ # This file contains checksums/hashes of SQLite so that we can verify # downloads using fetch command to setup.py have not been -# tampered with. See https://rogerbinns.github.io/apsw/build.html#additional-setup-py-flags +# tampered with. See https://rogerbinns.github.io/apsw/install.html#fetch # for information on setup.py flags. -# Format is URL length SHA1 MD5 all on one line. Yes SHA1 and MD5 are -# considered weakened, but it would take a fairly incredible feat to -# come up with something that has the same length and SHA1 and MD5 -# hashes. This extra checking mechanism is to give you a little bit +# Format is URL length sha256 sha3_256 all on one line. +# This extra checking mechanism is to give you a little bit # more peace of mind because the SQLite releases are not signed in any # way. -https://sqlite.org/2023/sqlite-autoconf-3410000.tar.gz 3124499 a7cb1364d5af52a8230c048877e657d49a8dda44 ff001143636ee7cf899020abdbc8cb89 -https://sqlite.org/2023/sqlite-autoconf-3410100.tar.gz 3125153 e77599a2df4c5b114c942ddba4483550d8982bf2 c6d5034cf39232299ccfdf8e3ddc5781 -https://sqlite.org/2023/sqlite-autoconf-3410200.tar.gz 3125545 f5a8a9ad5552b19fb5a41cf5536c8ab3b9bce82e 862075fd1c38324878ef809eda39edfe +https://sqlite.org/2023/sqlite-autoconf-3440200.tar.gz 3204841 1c6719a148bc41cf0f2bbbe3926d7ce3f5ca09d878f1246fcc20767b175bb407 6c427f0547e2f7babe636b748dd5d5a1f2f31601adadef7e2805e7d1f7171861 +https://sqlite.org/2023/sqlite-autoconf-3440100.tar.gz 3204737 63c3181633844adb5e36187f75b8f31a51cd32487992a26b89bf26b22ecdcf48 c405017524ddad7957ede0fc471986a7125b977adbcd7c5b785f2108dbee992d +https://sqlite.org/2023/sqlite-autoconf-3440000.tar.gz 3198005 b9cd386e7cd22af6e0d2a0f06d0404951e1bef109e42ea06cc0450e10cd15550 6869046465eae886f1a9f2c8debeeba44d34328693aa77a5bd4a3cfed93d6556 -https://sqlite.org/2023/sqlite-autoconf-3420000.tar.gz 3148813 036575929b174c1b829769255491ba2b32bda9ee 0c5a92bc51cf07cae45b4a1e94653dea +https://sqlite.org/2023/sqlite-autoconf-3430200.tar.gz 3177625 6d422b6f62c4de2ca80d61860e3a3fb693554d2f75bb1aaca743ccc4d6f609f0 a7463a45ed58849200858e514b79f7e5f5d69850047897c5b659a78a0bc75cc1 +https://sqlite.org/2023/sqlite-autoconf-3430100.tar.gz 3175971 39116c94e76630f22d54cd82c3cea308565f1715f716d1b2527f1c9c969ba4d9 fd32866c281539eae95cd90b5c2c34337d8808822a988211b9b009c1e788e42d +https://sqlite.org/2023/sqlite-autoconf-3430000.tar.gz 3178199 49008dbf3afc04d4edc8ecfc34e4ead196973034293c997adad2f63f01762ae1 cc321c7b0a70f87aaefe5d0aa89cdd97b432c3d2d448fa623f20988007c49f34 -https://sqlite.org/2023/sqlite-autoconf-3430000.tar.gz 3178199 59e8c695e34bf1bc9aeb076530f84f66d0f30a5e f321a958aed13fb5f8773ae2f3504c0b -https://sqlite.org/2023/sqlite-autoconf-3430100.tar.gz 3175971 194f57642f191939a16399208a10b5e0a827b916 77e61befe9c3298da0504f87772a24b0 -https://sqlite.org/2023/sqlite-autoconf-3430200.tar.gz 3177625 cf9fbab967e3d924c7d6fc5b8e4a23a81d067fa5 94fb06bfebc437762e489c355ae63716 +https://sqlite.org/2023/sqlite-autoconf-3420000.tar.gz 3148813 7abcfd161c6e2742ca5c6c0895d1f853c940f203304a0b49da4e1eca5d088ca6 643898e9fcc8f6069bcd47b0e6057221c1ed17bbee57da20d2752c79d91274e8 -https://sqlite.org/2023/sqlite-autoconf-3440000.tar.gz 3198005 5afebfa26abefb98c2fa4c3dc010260f398c176a 7d4a49f724ad0643f3c4bf7e5a5838c0 \ No newline at end of file +https://sqlite.org/2023/sqlite-autoconf-3410200.tar.gz 3125545 e98c100dd1da4e30fa460761dab7c0b91a50b785e167f8c57acc46514fae9499 1ebb5539dd6fde9a0f89e8ab765af0b9f02010fc6baf6985b54781a38c00020a +https://sqlite.org/2023/sqlite-autoconf-3410100.tar.gz 3125153 4dadfbeab9f8e16c695d4fbbc51c16b2f77fb97ff4c1c3d139919dfc038c9e33 38ecb6b086c5c1ee1e52b57556745055328ac912929ccade9deaefdd71033ddb +https://sqlite.org/2023/sqlite-autoconf-3410000.tar.gz 3124499 49f77ac53fd9aa5d7395f2499cb816410e5621984a121b858ccca05310b05c70 d783ab44a2b44394331d392b8b8d4d2ea4964cbb2befc7c6c649bcfbdb3c9ffe diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/doc/bestpractice.rst new/apsw-3.44.2.0/doc/bestpractice.rst --- old/apsw-3.44.0.0/doc/bestpractice.rst 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/doc/bestpractice.rst 2023-11-29 17:45:17.000000000 +0100 @@ -31,5 +31,6 @@ --- .. automodule:: apsw.bestpractice + :synopsis: Ensure SQLite usage prevents common mistakes, and get best performance :members: :undoc-members: \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/doc/changes.rst new/apsw-3.44.2.0/doc/changes.rst --- old/apsw-3.44.0.0/doc/changes.rst 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/doc/changes.rst 2023-11-29 17:45:17.000000000 +0100 @@ -10,6 +10,25 @@ APSW changes by version ----------------------- +3.44.2.0 +======== + +Added `logger` parameter to :func:`apsw.ext.log_sqlite` to use a +specific :class:`logging.Logger` (:issue:`493`) + +Added :func:`apsw.ext.result_string` to turn an result code into +a string, taking into account if it is extended or not. + +Provide detail when C implemented objects are printed. For example +:class:`connections <Connection>` include the filename. +(:issue:`494`) + +Added :meth:`URIFilename.parameters` (:issue:`496`) + +:class:`URIFilename` are only valid for the duration of the +:meth:`VFS.xOpen` call. If you save and use the object later you will +get an exception. (:issue:`501`) + 3.44.0.0 ======== @@ -17,7 +36,7 @@ On 64 bit platforms with the amalgamation, `SQLITE_MAX_MMAP_SIZE <https://www.sqlite.org/mmap.html>`__ is set to 256 terabytes. -SQLite's default limit is 2GB. (:issue:`491`)` +SQLite's default limit is 2GB. (:issue:`491`) 3.43.2.0 ======== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/doc/extensions.rst new/apsw-3.44.2.0/doc/extensions.rst --- old/apsw-3.44.0.0/doc/extensions.rst 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/doc/extensions.rst 2023-11-29 17:45:17.000000000 +0100 @@ -30,14 +30,6 @@ <https://sqlite.org/src/tree?name=ext/icu>`__ shows how to use it. -.. _ext-json1: - -JSON1 -===== - -`Provides functions <https://www.sqlite.org/json1.html>`__ for managing `JSON -<https://en.wikipedia.org/wiki/JSON>`__ data stored in SQLite. - .. _ext-rbu: RBU diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/doc/install.rst new/apsw-3.44.2.0/doc/install.rst --- old/apsw-3.44.0.0/doc/install.rst 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/doc/install.rst 2023-11-29 17:45:17.000000000 +0100 @@ -74,12 +74,12 @@ .. downloads-begin -* `apsw-3.44.0.0.zip - <https://github.com/rogerbinns/apsw/releases/download/3.44.0.0/apsw-3.44.0.0.zip>`__ +* `apsw-3.44.2.0.zip + <https://github.com/rogerbinns/apsw/releases/download/3.44.2.0/apsw-3.44.2.0.zip>`__ (Source, includes this HTML Help) -* `apsw-3.44.0.0-sigs.zip - <https://github.com/rogerbinns/apsw/releases/download/3.44.0.0/apsw-3.44.0.0-sigs.zip>`__ +* `apsw-3.44.2.0-sigs.zip + <https://github.com/rogerbinns/apsw/releases/download/3.44.2.0/apsw-3.44.2.0-sigs.zip>`__ GPG signatures for all files .. downloads-end @@ -104,7 +104,7 @@ .. code-block:: console - $ gpg --verify apsw-3.44.0.0.zip.asc + $ gpg --verify apsw-3.44.2.0.zip.asc gpg: Signature made ... date ... using DSA key ID 0DFBD904 gpg: Good signature from "Roger Binns <rog...@rogerbinns.com>" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/example-code.py new/apsw-3.44.2.0/example-code.py --- old/apsw-3.44.0.0/example-code.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/example-code.py 2023-11-29 17:45:17.000000000 +0100 @@ -880,13 +880,14 @@ ### vfs: VFS - Virtual File System # :ref:`VFS <vfs>` lets you control how SQLite accesses storage. APSW # makes it easy to "inherit" from an existing VFS and monitor or alter -# data as it flows through. You can also implement your own -# :class:`pragmas <VFSFcntlPragma>`. +# data as it flows through. +# +# :class:`URI <URIFilename>` are shown as a way to receive parameters +# when opening/creating a database file, and :class:`pragmas <VFSFcntlPragma>` +# for receiving parameters once a database is open. # This example VFS obfuscates the database file contents by xor all -# bytes with 0xa5. URI parameters are also shown as a way you can -# pass additional information for files. - +# bytes with 0xa5. def obfuscate(data: bytes): return bytes([x ^ 0xa5 for x in data]) @@ -916,6 +917,8 @@ print(" level is", name.uri_int("level", 3)) print(" warp is", name.uri_boolean("warp", False)) print(" notpresent is", name.uri_parameter("notpresent")) + # all of them + print(" all uris", name.parameters) else: print(" filename", name) return ObfuscatedVFSFile(self.base_vfs, name, flags) @@ -959,6 +962,7 @@ # add in using URI parameters open_flags |= apsw.SQLITE_OPEN_URI +# uri parameters are after the ? separated by & obfudb = apsw.Connection("file:myobfudb?fast=speed&level=7&warp=on&another=true", flags=open_flags, vfs=obfuvfs.vfs_name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/setup.py new/apsw-3.44.2.0/setup.py --- old/apsw-3.44.0.0/setup.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/setup.py 2023-11-29 17:45:17.000000000 +0100 @@ -353,17 +353,11 @@ # A function for verifying downloads def verifyurl(self, url, data): d = ["%s" % (len(data), )] - try: - import hashlib - d.append(hashlib.sha1(data).hexdigest()) - d.append(hashlib.md5(data).hexdigest()) - except ImportError: - import sha - d.append(sha.new(data).hexdigest()) - import md5 - d.append(md5.new(data).hexdigest()) + import hashlib + d.append(hashlib.sha256(data).hexdigest()) + d.append(hashlib.sha3_256(data).hexdigest()) - write(" Length:", d[0], " SHA1:", d[1], " MD5:", d[2]) + write(" Length:", d[0], " SHA256:", d[1], " SHA3_256:", d[2]) sums = os.path.join(os.path.dirname(__file__), "checksums") for line in read_whole_file(sums, "rt").split("\n"): line = line.strip() @@ -380,9 +374,9 @@ if l[1] != d[0]: write("Length does not match. Expected", l[1], "download was", d[0]) if l[2] != d[1]: - write("SHA does not match. Expected", l[2], "download was", d[1]) + write("SHA256 does not match. Expected", l[2], "download was", d[1]) if l[3] != d[2]: - write("MD5 does not match. Expected", l[3], "download was", d[2]) + write("SHA3_256 does not match. Expected", l[3], "download was", d[2]) write("The download does not match the checksums distributed with APSW.\n" "The download should not have changed since the checksums were\n" "generated. The cause could be anything from network corruption\n" @@ -533,7 +527,7 @@ if self.enable_all_extensions: exts = [ - "fts4", "fts3", "fts3_parenthesis", "rtree", "stat4", "json1", "fts5", "rbu", "geopoly", + "fts4", "fts3", "fts3_parenthesis", "rtree", "stat4", "fts5", "rbu", "geopoly", "math_functions" ] if not self.omit or "icu" not in self.omit.split(","): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/apsw.docstrings new/apsw-3.44.2.0/src/apsw.docstrings --- old/apsw-3.44.0.0/src/apsw.docstrings 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/apsw.docstrings 2023-11-29 17:45:17.000000000 +0100 @@ -1727,7 +1727,9 @@ #define Connection_pragma_DOC "pragma($self,name,value=None)\n--\n\nConnection.pragma(name: str, value: Optional[SQLiteValue] = None) -> Any\n\n" \ "Issues the pragma (with the value if supplied) and returns the result with\n" \ ":attr:`the least amount of structure <Cursor.get>`. For example\n" \ -":code:`pragma(\"user_version\")` will return just the number.\n" \ +":code:`pragma(\"user_version\")` will return just the number, while\n" \ +":code:`pragma(\"journal_mode\", \"WAL\")` will return the journal mode\n" \ +"now in effect.\n" \ "\n" \ "Pragmas do not support bindings, so this method is a convenient\n" \ "alternative to composing SQL text.\n" \ @@ -2863,11 +2865,19 @@ "or the main database flag is set.\n" \ "\n" \ "You can safely pass it on to the :class:`VFSFile` constructor\n" \ -"which knows how to get the name back out.\n" +"which knows how to get the name back out. The URIFilename is\n" \ +"only valid for the duration of the xOpen call. If you save\n" \ +"and use the object later you will get an exception.\n" #define URIFilename_filename_DOC "filename($self)\n--\n\nURIFilename.filename() -> str\n\n" \ "Returns the filename.\n" +#define URIFilename_parameters_DOC ":type: tuple[str, ...]\n" \ +"\n" \ +"A tuple of the parameter names present.\n" \ +"\n" \ +"Calls: `sqlite3_uri_key <https://sqlite.org/c3ref/uri_boolean.html>`__\n" + #define URIFilename_uri_boolean_DOC "uri_boolean($self,name,default)\n--\n\nURIFilename.uri_boolean(name: str, default: bool) -> bool\n\n" \ "Returns the boolean value for parameter `name` or `default` if not\n" \ "present.\n" \ @@ -2955,7 +2965,7 @@ ":func:`sys.unraisablehook` and :func:`sys.excepthook`, falling back to\n" \ "`PyErr_Display`.\n" -#define VFSFile_init_DOC "__init__($self,vfs,filename,flags)\n--\n\nVFSFile.__init__(vfs: str, filename: str | URIFilename, flags: list[int, int])\n\n" \ +#define VFSFile_init_DOC "__init__($self,vfs,filename,flags)\n--\n\nVFSFile.__init__(vfs: str, filename: str | URIFilename, flags: list[int,int])\n\n" \ ":param vfs: The vfs you want to inherit behaviour from. You can\n" \ " use an empty string ``\"\"`` to inherit from the default vfs.\n" \ ":param name: The name of the file being opened. May be an instance of :class:`URIFilename`.\n" \ @@ -2974,7 +2984,7 @@ " :meth:`VFS.xOpen`\n" #define VFSFile_init_KWNAMES "vfs", "filename", "flags" -#define VFSFile_init_USAGE "VFSFile.__init__(vfs: str, filename: str | URIFilename, flags: list[int, int])" +#define VFSFile_init_USAGE "VFSFile.__init__(vfs: str, filename: str | URIFilename, flags: list[int,int])" #define VFSFile_init_CHECK do { \ assert(__builtin_types_compatible_p(typeof(vfs), const char *)); \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/apswversion.h new/apsw-3.44.2.0/src/apswversion.h --- old/apsw-3.44.0.0/src/apswversion.h 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/apswversion.h 2023-11-29 17:45:17.000000000 +0100 @@ -1 +1 @@ -#define APSW_VERSION "3.44.0.0" +#define APSW_VERSION "3.44.2.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/backup.c new/apsw-3.44.2.0/src/backup.c --- old/apsw-3.44.0.0/src/backup.c 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/backup.c 2023-11-29 17:45:17.000000000 +0100 @@ -346,6 +346,15 @@ Py_RETURN_FALSE; } +static PyObject * +APSWBackup_tp_str(APSWBackup *self) +{ + return PyUnicode_FromFormat("<apsw.Backup object from %S to %S at %p>", + self->source ? (PyObject *)self->source : apst.closed, + self->dest ? (PyObject *)self->dest : apst.closed, + self); +} + /** .. attribute:: done :type: bool @@ -390,4 +399,5 @@ .tp_methods = backup_methods, .tp_members = backup_members, .tp_getset = backup_getset, + .tp_str = (reprfunc)APSWBackup_tp_str, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/blob.c new/apsw-3.44.2.0/src/blob.c --- old/apsw-3.44.0.0/src/blob.c 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/blob.c 2023-11-29 17:45:17.000000000 +0100 @@ -92,6 +92,14 @@ return PyLong_FromLong(self->blobsize); } +static PyObject * +ZeroBlobBind_tp_str(ZeroBlobBind *self) +{ + return PyUnicode_FromFormat("<apsw.zeroblob object size %lld at %p>", + self->blobsize, + self); +} + static PyMethodDef ZeroBlobBind_methods[] = { {"length", (PyCFunction)ZeroBlobBind_len, METH_NOARGS, Zeroblob_length_DOC}, @@ -106,6 +114,7 @@ .tp_methods = ZeroBlobBind_methods, .tp_init = (initproc)ZeroBlobBind_init, .tp_new = ZeroBlobBind_new, + .tp_str = (reprfunc)ZeroBlobBind_tp_str, }; /* BLOB TYPE */ @@ -664,6 +673,14 @@ Py_RETURN_NONE; } +static PyObject * +APSWBlob_tp_str(APSWBlob *self) +{ + return PyUnicode_FromFormat("<apsw.Blob object from %S at %p>", + self->connection ? (PyObject *)self->connection : apst.closed, + self); +} + static PyMethodDef APSWBlob_methods[] = { {"length", (PyCFunction)APSWBlob_length, METH_NOARGS, Blob_length_DOC}, @@ -701,4 +718,5 @@ .tp_doc = Blob_class_DOC, .tp_weaklistoffset = offsetof(APSWBlob, weakreflist), .tp_methods = APSWBlob_methods, + .tp_str = (reprfunc)APSWBlob_tp_str, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/connection.c new/apsw-3.44.2.0/src/connection.c --- old/apsw-3.44.0.0/src/connection.c 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/connection.c 2023-11-29 17:45:17.000000000 +0100 @@ -2435,7 +2435,7 @@ return 1; } - PyErr_Format(PyExc_TypeError, "Bad return type from function callback"); + PyErr_Format(PyExc_TypeError, "Value from Python is not supported by SQLite. It should be one of None, int, float, str, or bytes. Received %s.", Py_TypeName(obj)); sqlite3_result_error(context, "Bad return type from python function callback", -1); return 0; } @@ -4383,7 +4383,9 @@ Issues the pragma (with the value if supplied) and returns the result with :attr:`the least amount of structure <Cursor.get>`. For example - :code:`pragma("user_version")` will return just the number. + :code:`pragma("user_version")` will return just the number, while + :code:`pragma("journal_mode", "WAL")` will return the journal mode + now in effect. Pragmas do not support bindings, so this method is a convenient alternative to composing SQL text. @@ -5164,6 +5166,16 @@ return 0; } +static PyObject * +Connection_tp_str(Connection *self) +{ + return PyUnicode_FromFormat("<apsw.Connection object %s%s%s at %p>", + self->db ? "\"" : "(", + self->db ? sqlite3_db_filename(self->db, "main") : "closed", + self->db ? "\"" : ")", + self); +} + static PyMemberDef Connection_members[] = { /* name type offset flags doc */ {"open_flags", T_OBJECT, offsetof(Connection, open_flags), READONLY, Connection_open_flags_DOC}, @@ -5359,4 +5371,5 @@ .tp_getset = Connection_getseters, .tp_init = (initproc)Connection_init, .tp_new = Connection_new, + .tp_str = (reprfunc)Connection_tp_str, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/constants.c new/apsw-3.44.2.0/src/constants.c --- old/apsw-3.44.0.0/src/constants.c 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/constants.c 2023-11-29 17:45:17.000000000 +0100 @@ -4,6 +4,11 @@ Deal with those - do not edit this file */ +/* added in 3.44.1.0 */ +#ifndef SQLITE_RESULT_SUBTYPE +#define SQLITE_RESULT_SUBTYPE 0x001000000 +#endif + /* returns zero on success, -1 on error */ static int add_apsw_constants(PyObject *module) @@ -433,10 +438,11 @@ /* Function Flags */ the_dict = Py_BuildValue( - "{siissiissiissiis}", + "{siissiissiissiissiis}", "SQLITE_DETERMINISTIC", SQLITE_DETERMINISTIC, SQLITE_DETERMINISTIC, "SQLITE_DETERMINISTIC", "SQLITE_DIRECTONLY", SQLITE_DIRECTONLY, SQLITE_DIRECTONLY, "SQLITE_DIRECTONLY", "SQLITE_INNOCUOUS", SQLITE_INNOCUOUS, SQLITE_INNOCUOUS, "SQLITE_INNOCUOUS", + "SQLITE_RESULT_SUBTYPE", SQLITE_RESULT_SUBTYPE, SQLITE_RESULT_SUBTYPE, "SQLITE_RESULT_SUBTYPE", "SQLITE_SUBTYPE", SQLITE_SUBTYPE, SQLITE_SUBTYPE, "SQLITE_SUBTYPE"); if (!the_dict) { @@ -1106,6 +1112,7 @@ || PyModule_AddIntConstant(module, "SQLITE_RECURSIVE", SQLITE_RECURSIVE) || PyModule_AddIntConstant(module, "SQLITE_REINDEX", SQLITE_REINDEX) || PyModule_AddIntConstant(module, "SQLITE_REPLACE", SQLITE_REPLACE) + || PyModule_AddIntConstant(module, "SQLITE_RESULT_SUBTYPE", SQLITE_RESULT_SUBTYPE) || PyModule_AddIntConstant(module, "SQLITE_ROLLBACK", SQLITE_ROLLBACK) || PyModule_AddIntConstant(module, "SQLITE_ROW", SQLITE_ROW) || PyModule_AddIntConstant(module, "SQLITE_SAVEPOINT", SQLITE_SAVEPOINT) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/cursor.c new/apsw-3.44.2.0/src/cursor.c --- old/apsw-3.44.0.0/src/cursor.c 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/cursor.c 2023-11-29 17:45:17.000000000 +0100 @@ -1669,6 +1669,14 @@ return NULL; } +static PyObject * +APSWCursor_tp_str(APSWCursor *self) +{ + return PyUnicode_FromFormat("<apsw.Cursor object from %S at %p>", + self->connection ? (PyObject *)self->connection : apst.closed, + self); +} + static PyMethodDef APSWCursor_methods[] = { {"execute", (PyCFunction)APSWCursor_execute, METH_FASTCALL | METH_KEYWORDS, Cursor_execute_DOC}, @@ -1741,4 +1749,5 @@ .tp_getset = APSWCursor_getset, .tp_init = (initproc)APSWCursor_init, .tp_new = APSWCursor_new, + .tp_str = (reprfunc)APSWCursor_tp_str, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/stringconstants.c new/apsw-3.44.2.0/src/stringconstants.c --- old/apsw-3.44.0.0/src/stringconstants.c 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/stringconstants.c 2023-11-29 17:45:17.000000000 +0100 @@ -6,6 +6,7 @@ static struct _apsw_string_table { + PyObject *closed; PyObject *s_1e999; PyObject *s0_0; PyObject *s1e999; @@ -87,6 +88,7 @@ static void fini_apsw_strings(void) { + Py_CLEAR(apst.closed); Py_CLEAR(apst.s_1e999); Py_CLEAR(apst.s0_0); Py_CLEAR(apst.s1e999); @@ -169,7 +171,7 @@ static int init_apsw_strings() { - if ((0 == (apst.s_1e999 = PyUnicode_FromString("-1e999"))) || (0 == (apst.s0_0 = PyUnicode_FromString("0.0"))) || (0 == (apst.s1e999 = PyUnicode_FromString("1e999"))) || (0 == (apst.Begin = PyUnicode_FromString("Begin"))) || (0 == (apst.BestIndex = PyUnicode_FromString("BestIndex"))) || (0 == (apst.BestIndexObject = PyUnicode_FromString("BestIndexObject"))) || (0 == (apst.Close = PyUnicode_FromString("Close"))) || (0 == (apst.Column = PyUnicode_FromString("Column"))) || (0 == (apst.ColumnNoChange = PyUnicode_FromString("ColumnNoChange"))) || (0 == (apst.Commit = PyUnicode_FromString("Commit"))) || (0 == (apst.Connect = PyUnicode_FromString("Connect"))) || (0 == (apst.Create = PyUnicode_FromString("Create"))) || (0 == (apst.Destroy = PyUnicode_FromString("Destroy"))) || (0 == (apst.Disconnect = PyUnicode_FromString("Disconnect"))) || (0 == (apst.Eof = PyUnicode_FromString("Eof"))) || (0 == (apst.Filter = PyUnicode_FromString("Filter"))) || (0 == (apst.FindFunction = PyUnicode_Fro mString("FindFunction"))) || (0 == (apst.Integrity = PyUnicode_FromString("Integrity"))) || (0 == (apst.Mapping = PyUnicode_FromString("Mapping"))) || (0 == (apst.sNULL = PyUnicode_FromString("NULL"))) || (0 == (apst.Next = PyUnicode_FromString("Next"))) || (0 == (apst.Open = PyUnicode_FromString("Open"))) || (0 == (apst.Release = PyUnicode_FromString("Release"))) || (0 == (apst.Rename = PyUnicode_FromString("Rename"))) || (0 == (apst.Rollback = PyUnicode_FromString("Rollback"))) || (0 == (apst.RollbackTo = PyUnicode_FromString("RollbackTo"))) || (0 == (apst.Rowid = PyUnicode_FromString("Rowid"))) || (0 == (apst.Savepoint = PyUnicode_FromString("Savepoint"))) || (0 == (apst.ShadowName = PyUnicode_FromString("ShadowName"))) || (0 == (apst.Sync = PyUnicode_FromString("Sync"))) || (0 == (apst.UpdateChangeRow = PyUnicode_FromString("UpdateChangeRow"))) || (0 == (apst.UpdateDeleteRow = PyUnicode_FromString("UpdateDeleteRow"))) || (0 == (apst.UpdateInsertRow = PyUnicode_FromString("Update InsertRow"))) || (0 == (apst.add_note = PyUnicode_FromString("add_note"))) || (0 == (apst.close = PyUnicode_FromString("close"))) || (0 == (apst.connection_hooks = PyUnicode_FromString("connection_hooks"))) || (0 == (apst.cursor = PyUnicode_FromString("cursor"))) || (0 == (apst.error_offset = PyUnicode_FromString("error_offset"))) || (0 == (apst.excepthook = PyUnicode_FromString("excepthook"))) || (0 == (apst.execute = PyUnicode_FromString("execute"))) || (0 == (apst.executemany = PyUnicode_FromString("executemany"))) || (0 == (apst.extendedresult = PyUnicode_FromString("extendedresult"))) || (0 == (apst.final = PyUnicode_FromString("final"))) || (0 == (apst.get = PyUnicode_FromString("get"))) || (0 == (apst.inverse = PyUnicode_FromString("inverse"))) || (0 == (apst.result = PyUnicode_FromString("result"))) || (0 == (apst.step = PyUnicode_FromString("step"))) || (0 == (apst.value = PyUnicode_FromString("value"))) || (0 == (apst.xAccess = PyUnicode_FromString("xAccess"))) || (0 == (a pst.xCheckReservedLock = PyUnicode_FromString("xCheckReservedLock"))) || (0 == (apst.xClose = PyUnicode_FromString("xClose"))) || (0 == (apst.xCurrentTime = PyUnicode_FromString("xCurrentTime"))) || (0 == (apst.xCurrentTimeInt64 = PyUnicode_FromString("xCurrentTimeInt64"))) || (0 == (apst.xDelete = PyUnicode_FromString("xDelete"))) || (0 == (apst.xDeviceCharacteristics = PyUnicode_FromString("xDeviceCharacteristics"))) || (0 == (apst.xDlClose = PyUnicode_FromString("xDlClose"))) || (0 == (apst.xDlError = PyUnicode_FromString("xDlError"))) || (0 == (apst.xDlOpen = PyUnicode_FromString("xDlOpen"))) || (0 == (apst.xDlSym = PyUnicode_FromString("xDlSym"))) || (0 == (apst.xFileControl = PyUnicode_FromString("xFileControl"))) || (0 == (apst.xFileSize = PyUnicode_FromString("xFileSize"))) || (0 == (apst.xFullPathname = PyUnicode_FromString("xFullPathname"))) || (0 == (apst.xGetLastError = PyUnicode_FromString("xGetLastError"))) || (0 == (apst.xGetSystemCall = PyUnicode_FromString("xGetSyst emCall"))) || (0 == (apst.xLock = PyUnicode_FromString("xLock"))) || (0 == (apst.xNextSystemCall = PyUnicode_FromString("xNextSystemCall"))) || (0 == (apst.xOpen = PyUnicode_FromString("xOpen"))) || (0 == (apst.xRandomness = PyUnicode_FromString("xRandomness"))) || (0 == (apst.xRead = PyUnicode_FromString("xRead"))) || (0 == (apst.xSectorSize = PyUnicode_FromString("xSectorSize"))) || (0 == (apst.xSetSystemCall = PyUnicode_FromString("xSetSystemCall"))) || (0 == (apst.xSleep = PyUnicode_FromString("xSleep"))) || (0 == (apst.xSync = PyUnicode_FromString("xSync"))) || (0 == (apst.xTruncate = PyUnicode_FromString("xTruncate"))) || (0 == (apst.xUnlock = PyUnicode_FromString("xUnlock"))) || (0 == (apst.xWrite = PyUnicode_FromString("xWrite")))) + if ((0 == (apst.closed = PyUnicode_FromString("(closed)"))) || (0 == (apst.s_1e999 = PyUnicode_FromString("-1e999"))) || (0 == (apst.s0_0 = PyUnicode_FromString("0.0"))) || (0 == (apst.s1e999 = PyUnicode_FromString("1e999"))) || (0 == (apst.Begin = PyUnicode_FromString("Begin"))) || (0 == (apst.BestIndex = PyUnicode_FromString("BestIndex"))) || (0 == (apst.BestIndexObject = PyUnicode_FromString("BestIndexObject"))) || (0 == (apst.Close = PyUnicode_FromString("Close"))) || (0 == (apst.Column = PyUnicode_FromString("Column"))) || (0 == (apst.ColumnNoChange = PyUnicode_FromString("ColumnNoChange"))) || (0 == (apst.Commit = PyUnicode_FromString("Commit"))) || (0 == (apst.Connect = PyUnicode_FromString("Connect"))) || (0 == (apst.Create = PyUnicode_FromString("Create"))) || (0 == (apst.Destroy = PyUnicode_FromString("Destroy"))) || (0 == (apst.Disconnect = PyUnicode_FromString("Disconnect"))) || (0 == (apst.Eof = PyUnicode_FromString("Eof"))) || (0 == (apst.Filter = PyUnicode_FromStr ing("Filter"))) || (0 == (apst.FindFunction = PyUnicode_FromString("FindFunction"))) || (0 == (apst.Integrity = PyUnicode_FromString("Integrity"))) || (0 == (apst.Mapping = PyUnicode_FromString("Mapping"))) || (0 == (apst.sNULL = PyUnicode_FromString("NULL"))) || (0 == (apst.Next = PyUnicode_FromString("Next"))) || (0 == (apst.Open = PyUnicode_FromString("Open"))) || (0 == (apst.Release = PyUnicode_FromString("Release"))) || (0 == (apst.Rename = PyUnicode_FromString("Rename"))) || (0 == (apst.Rollback = PyUnicode_FromString("Rollback"))) || (0 == (apst.RollbackTo = PyUnicode_FromString("RollbackTo"))) || (0 == (apst.Rowid = PyUnicode_FromString("Rowid"))) || (0 == (apst.Savepoint = PyUnicode_FromString("Savepoint"))) || (0 == (apst.ShadowName = PyUnicode_FromString("ShadowName"))) || (0 == (apst.Sync = PyUnicode_FromString("Sync"))) || (0 == (apst.UpdateChangeRow = PyUnicode_FromString("UpdateChangeRow"))) || (0 == (apst.UpdateDeleteRow = PyUnicode_FromString("UpdateDeleteRow"))) || (0 == (apst.UpdateInsertRow = PyUnicode_FromString("UpdateInsertRow"))) || (0 == (apst.add_note = PyUnicode_FromString("add_note"))) || (0 == (apst.close = PyUnicode_FromString("close"))) || (0 == (apst.connection_hooks = PyUnicode_FromString("connection_hooks"))) || (0 == (apst.cursor = PyUnicode_FromString("cursor"))) || (0 == (apst.error_offset = PyUnicode_FromString("error_offset"))) || (0 == (apst.excepthook = PyUnicode_FromString("excepthook"))) || (0 == (apst.execute = PyUnicode_FromString("execute"))) || (0 == (apst.executemany = PyUnicode_FromString("executemany"))) || (0 == (apst.extendedresult = PyUnicode_FromString("extendedresult"))) || (0 == (apst.final = PyUnicode_FromString("final"))) || (0 == (apst.get = PyUnicode_FromString("get"))) || (0 == (apst.inverse = PyUnicode_FromString("inverse"))) || (0 == (apst.result = PyUnicode_FromString("result"))) || (0 == (apst.step = PyUnicode_FromString("step"))) || (0 == (apst.value = PyUnicode_FromString("value"))) || (0 == (a pst.xAccess = PyUnicode_FromString("xAccess"))) || (0 == (apst.xCheckReservedLock = PyUnicode_FromString("xCheckReservedLock"))) || (0 == (apst.xClose = PyUnicode_FromString("xClose"))) || (0 == (apst.xCurrentTime = PyUnicode_FromString("xCurrentTime"))) || (0 == (apst.xCurrentTimeInt64 = PyUnicode_FromString("xCurrentTimeInt64"))) || (0 == (apst.xDelete = PyUnicode_FromString("xDelete"))) || (0 == (apst.xDeviceCharacteristics = PyUnicode_FromString("xDeviceCharacteristics"))) || (0 == (apst.xDlClose = PyUnicode_FromString("xDlClose"))) || (0 == (apst.xDlError = PyUnicode_FromString("xDlError"))) || (0 == (apst.xDlOpen = PyUnicode_FromString("xDlOpen"))) || (0 == (apst.xDlSym = PyUnicode_FromString("xDlSym"))) || (0 == (apst.xFileControl = PyUnicode_FromString("xFileControl"))) || (0 == (apst.xFileSize = PyUnicode_FromString("xFileSize"))) || (0 == (apst.xFullPathname = PyUnicode_FromString("xFullPathname"))) || (0 == (apst.xGetLastError = PyUnicode_FromString("xGetLastError"))) || (0 == (apst.xGetSystemCall = PyUnicode_FromString("xGetSystemCall"))) || (0 == (apst.xLock = PyUnicode_FromString("xLock"))) || (0 == (apst.xNextSystemCall = PyUnicode_FromString("xNextSystemCall"))) || (0 == (apst.xOpen = PyUnicode_FromString("xOpen"))) || (0 == (apst.xRandomness = PyUnicode_FromString("xRandomness"))) || (0 == (apst.xRead = PyUnicode_FromString("xRead"))) || (0 == (apst.xSectorSize = PyUnicode_FromString("xSectorSize"))) || (0 == (apst.xSetSystemCall = PyUnicode_FromString("xSetSystemCall"))) || (0 == (apst.xSleep = PyUnicode_FromString("xSleep"))) || (0 == (apst.xSync = PyUnicode_FromString("xSync"))) || (0 == (apst.xTruncate = PyUnicode_FromString("xTruncate"))) || (0 == (apst.xUnlock = PyUnicode_FromString("xUnlock"))) || (0 == (apst.xWrite = PyUnicode_FromString("xWrite")))) { fini_apsw_strings(); return -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/src/vfs.c new/apsw-3.44.2.0/src/vfs.c --- old/apsw-3.44.0.0/src/vfs.c 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/src/vfs.c 2023-11-29 17:45:17.000000000 +0100 @@ -295,7 +295,7 @@ typedef struct { PyObject_HEAD struct sqlite3_file *base; - /* filename as to be around for lifetime of base. This will + /* filename has to be around for lifetime of base. This will either be utf8 text (a string was passed in) or point to the filename in APSWURIFilename. The former needs to be freed, the latter not. @@ -639,6 +639,9 @@ PyObject *vargs[] = {NULL, (PyObject *)(vfs->pAppData), nameobject, flags}; if (vargs[2] && vargs[3]) pyresult = PyObject_VectorcallMethod(apst.xOpen, vargs + 1, 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + /* issue 501 */ + if (inflags & (SQLITE_OPEN_URI | SQLITE_OPEN_MAIN_DB)) + ((APSWURIFilename *)nameobject)->filename = 0; if (!pyresult) { result = MakeSqliteMsgFromPyException(NULL); @@ -1894,6 +1897,16 @@ return -1; } +static PyObject * +APSWVFS_tp_str(APSWVFS *self) +{ + if (!self->containingvfs) + return PyUnicode_FromFormat("<apsw.VFS object at %p>", self); + if (self->basevfs) + return PyUnicode_FromFormat("<apsw.VFS object \"%s\" inherits from \"%s\" at %p>", self->containingvfs->zName, self->basevfs->zName, self); + return PyUnicode_FromFormat("<apsw.VFS object \"%s\" at %p>", self->containingvfs->zName, self); +} + static PyMethodDef APSWVFS_methods[] = { {"xDelete", (PyCFunction)apswvfspy_xDelete, METH_FASTCALL | METH_KEYWORDS, VFS_xDelete_DOC}, {"xFullPathname", (PyCFunction)apswvfspy_xFullPathname, METH_FASTCALL | METH_KEYWORDS, VFS_xFullPathname_DOC}, @@ -1927,6 +1940,7 @@ .tp_methods = APSWVFS_methods, .tp_init = (initproc)APSWVFS_init, .tp_new = APSWVFS_new, + .tp_str = (reprfunc)APSWVFS_tp_str, }; static int is_apsw_vfs(sqlite3_vfs *vfs) @@ -1996,7 +2010,7 @@ return (PyObject *)self; } -/** .. method:: __init__(vfs: str, filename: str | URIFilename, flags: list[int, int]) +/** .. method:: __init__(vfs: str, filename: str | URIFilename, flags: list[int,int]) :param vfs: The vfs you want to inherit behaviour from. You can use an empty string ``""`` to inherit from the default vfs. @@ -2896,6 +2910,12 @@ return NULL; } +static PyObject * +APSWVFSFile_tp_str(APSWVFSFile *self) +{ + return PyUnicode_FromFormat("<apsw.VFSFile object filename \"%s\" at %p>", self->filename ? self->filename : "(nil)", self); +} + #define APSWPROXYBASE \ APSWSQLite3File *apswfile = (APSWSQLite3File *)(void *)file; \ APSWVFSFile *f = (APSWVFSFile *)(apswfile->file); \ @@ -2999,6 +3019,7 @@ .tp_methods = APSWVFSFile_methods, .tp_init = (initproc)APSWVFSFile_init, .tp_new = APSWVFSFile_new, + .tp_str = (reprfunc)APSWVFSFile_tp_str, }; /** .. class:: URIFilename @@ -3013,9 +3034,18 @@ or the main database flag is set. You can safely pass it on to the :class:`VFSFile` constructor - which knows how to get the name back out. + which knows how to get the name back out. The URIFilename is + only valid for the duration of the xOpen call. If you save + and use the object later you will get an exception. */ +#define CHECK_SCOPE \ + do \ + { \ + if (!self->filename) \ + return PyErr_Format(PyExc_ValueError, "URIFilename is out of scope"); \ + } while (0) + /** .. method:: filename() -> str Returns the filename. @@ -3023,9 +3053,46 @@ static PyObject * apswurifilename_filename(APSWURIFilename *self) { + CHECK_SCOPE; return convertutf8string(self->filename); } +/** .. attribute:: parameters + :type: tuple[str, ...] + + A tuple of the parameter names present. + + -* sqlite3_uri_key +*/ +static PyObject * +apswurifilename_parameters(APSWURIFilename *self) +{ + CHECK_SCOPE; + int i, count = 0; + for (i = 0;; i++) + if (!sqlite3_uri_key(self->filename, i)) + break; + count = i; + + PyObject *res = PyTuple_New(count); + if (!res) + goto fail; + + for (i = 0; i < count; i++) + { + PyObject *tmpstring = PyUnicode_FromString(sqlite3_uri_key(self->filename, i)); + if (!tmpstring) + goto fail; + PyTuple_SET_ITEM(res, i, tmpstring); + } + + return res; + +fail: + Py_XDECREF(res); + return NULL; +} + /** .. method:: uri_parameter(name: str) -> Optional[str] Returns the value of parameter `name` or None. @@ -3035,6 +3102,7 @@ static PyObject * apswurifilename_uri_parameter(APSWURIFilename *self, PyObject *const *fast_args, Py_ssize_t fast_nargs, PyObject *fast_kwnames) { + CHECK_SCOPE; const char *res, *name; { URIFilename_uri_parameter_CHECK; @@ -3056,6 +3124,7 @@ static PyObject * apswurifilename_uri_int(APSWURIFilename *self, PyObject *const *fast_args, Py_ssize_t fast_nargs, PyObject *fast_kwnames) { + CHECK_SCOPE; const char *name = NULL; long long res = 0, default_; @@ -3081,6 +3150,7 @@ static PyObject * apswurifilename_uri_boolean(APSWURIFilename *self, PyObject *const *fast_args, Py_ssize_t fast_nargs, PyObject *fast_kwnames) { + CHECK_SCOPE; const char *name = NULL; int default_ = 0, res; @@ -3099,6 +3169,15 @@ Py_RETURN_FALSE; } +static PyObject * +apswurifilename_tp_str(APSWURIFilename *self) +{ + /* CHECK_SCOPE not needed since we manually check */ + if (!self->filename) + return PyUnicode_FromFormat("<apsw.URIFilename object (out of scope) at %p>", self); + return PyUnicode_FromFormat("<apsw.URIFilename object \"%s\" at %p>", self->filename, self); +} + static PyMethodDef APSWURIFilenameMethods[] = { {"filename", (PyCFunction)apswurifilename_filename, METH_NOARGS, URIFilename_filename_DOC}, {"uri_parameter", (PyCFunction)apswurifilename_uri_parameter, METH_FASTCALL | METH_KEYWORDS, URIFilename_uri_parameter_DOC}, @@ -3107,6 +3186,11 @@ /* Sentinel */ {0, 0, 0, 0}}; +static PyGetSetDef APSWURIFilename_getset[] = { + {"parameters", (getter)apswurifilename_parameters, NULL, URIFilename_parameters_DOC}, + {0, 0, 0, 0}, +}; + static PyTypeObject APSWURIFilenameType = { PyVarObject_HEAD_INIT(NULL, 0) @@ -3115,4 +3199,8 @@ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = URIFilename_class_DOC, .tp_methods = APSWURIFilenameMethods, + .tp_str = (reprfunc)apswurifilename_tp_str, + .tp_getset = APSWURIFilename_getset, }; + +#undef CHECK_SCOPE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/tools/checksums.py new/apsw-3.44.2.0/tools/checksums.py --- old/apsw-3.44.0.0/tools/checksums.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/tools/checksums.py 2023-11-29 17:45:17.000000000 +0100 @@ -11,6 +11,8 @@ import setup sqlitevers = ( + '3440200', + '3440100', '3440000', '3430200', '3430100', @@ -39,7 +41,7 @@ def check(url, data): - d = ["%s" % (len(data), ), hashlib.sha1(data).hexdigest(), hashlib.md5(data).hexdigest()] + d = ["%s" % (len(data), ), hashlib.sha256(data).hexdigest(), hashlib.sha3_256(data).hexdigest()] line = getline(url) if line: if line != d: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/tools/docmissing.py new/apsw-3.44.2.0/tools/docmissing.py --- old/apsw-3.44.0.0/tools/docmissing.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/tools/docmissing.py 2023-11-29 17:45:17.000000000 +0100 @@ -54,7 +54,8 @@ ('VFS', vfs), ('VFSFile', vfsfile), ('apsw', apsw), - ('VFSFcntlPragma', apsw.VFSFcntlPragma) + ('VFSFcntlPragma', apsw.VFSFcntlPragma), + ("zeroblob", apsw.zeroblob(3)), ): if name not in classes: retval = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/tools/fi.py new/apsw-3.44.2.0/tools/fi.py --- old/apsw-3.44.0.0/tools/fi.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/tools/fi.py 2023-11-29 17:45:17.000000000 +0100 @@ -434,6 +434,7 @@ class myvfsfile(apsw.VFSFile): def __init__(self, parent, filename, flags): + filename.parameters super().__init__(parent, filename, flags) vfsinstance = myvfs() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/tools/find_unwrapped_apis.py new/apsw-3.44.2.0/tools/find_unwrapped_apis.py --- old/apsw-3.44.0.0/tools/find_unwrapped_apis.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/tools/find_unwrapped_apis.py 2023-11-29 17:45:17.000000000 +0100 @@ -59,7 +59,6 @@ "sqlite3_stmt_busy", "sqlite3_keyword_check", # sqlite3_keyword_name is used "sqlite3_compileoption_used", # sqlite3_compileoption_get is used - "sqlite3_uri_key", "sqlite3_set_auxdata", "sqlite3_get_auxdata", "sqlite3_value_dup", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/tools/genconstants.py new/apsw-3.44.2.0/tools/genconstants.py --- old/apsw-3.44.0.0/tools/genconstants.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/tools/genconstants.py 2023-11-29 17:45:17.000000000 +0100 @@ -85,6 +85,11 @@ Deal with those - do not edit this file */ +/* added in 3.44.1.0 */ +#ifndef SQLITE_RESULT_SUBTYPE +#define SQLITE_RESULT_SUBTYPE 0x001000000 +#endif + /* returns zero on success, -1 on error */ static int add_apsw_constants(PyObject *module) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/tools/genstrings.py new/apsw-3.44.2.0/tools/genstrings.py --- old/apsw-3.44.0.0/tools/genstrings.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/tools/genstrings.py 2023-11-29 17:45:17.000000000 +0100 @@ -39,6 +39,7 @@ step final value inverse NULL 0.0 -1e999 1e999 +(closed) """ # we have to make some valid C identifiers @@ -48,6 +49,7 @@ "0.0": "s0_0", "-1e999": "s_1e999", "1e999": "s1e999", + "(closed)": "closed", }.get(name, name) # tokenize names diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apsw-3.44.0.0/tools/megatest.py new/apsw-3.44.2.0/tools/megatest.py --- old/apsw-3.44.0.0/tools/megatest.py 2023-11-06 19:28:20.000000000 +0100 +++ new/apsw-3.44.2.0/tools/megatest.py 2023-11-29 17:45:17.000000000 +0100 @@ -201,7 +201,7 @@ # Default versions we support PYVERS = ( - '3.13.0a1', + '3.13.0a2', '3.12.0', '3.11.6', '3.10.13', @@ -210,7 +210,7 @@ 'system', ) -SQLITEVERS = ('3.44.0', ) +SQLITEVERS = ('3.44.0', '3.44.1', '3.44.2',) BITS = (64, 32)