apsw-sqlite3mc 3.53.0.0__tar.gz → 3.53.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/PKG-INFO +2 -2
  2. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/README.rst +1 -1
  3. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/__init__.pyi +3 -3
  4. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/aio.py +1 -5
  5. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/ext.py +2 -2
  6. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/__main__.py +1 -0
  7. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/aiotest.py +204 -0
  8. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/jsonb.py +4 -4
  9. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/mctests.py +5 -1
  10. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw_sqlite3mc.egg-info/PKG-INFO +2 -2
  11. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/checksums +3 -1
  12. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/setup.py +1 -1
  13. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/sqlite3/sqlite3.c +155 -96
  14. apsw_sqlite3mc-3.53.1.0/src/apswversion.h +1 -0
  15. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/cursor.c +131 -57
  16. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/faultinject.h +16 -0
  17. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/tools/vend.py +2 -4
  18. apsw_sqlite3mc-3.53.0.0/src/apswversion.h +0 -1
  19. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/LICENSE +0 -0
  20. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/MANIFEST.in +0 -0
  21. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/__main__.py +0 -0
  22. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/bestpractice.py +0 -0
  23. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/fts5.py +0 -0
  24. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/fts5aux.py +0 -0
  25. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/fts5query.py +0 -0
  26. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/fts_test_strings +0 -0
  27. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/mcall.py +0 -0
  28. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/py.typed +0 -0
  29. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/shell.py +0 -0
  30. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/speedtest.py +0 -0
  31. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/sqlite_extra.json +0 -0
  32. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/sqlite_extra.py +0 -0
  33. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/sqlite_extra_binaries/README.md +0 -0
  34. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/async_meta.py +0 -0
  35. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/carray.py +0 -0
  36. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/extratest.py +0 -0
  37. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/fork_checker.py +0 -0
  38. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/ftstests.py +0 -0
  39. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/sessiontests.py +0 -0
  40. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/tests/shelltest.py +0 -0
  41. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/trace.py +0 -0
  42. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw/unicode.py +0 -0
  43. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw_sqlite3mc.egg-info/SOURCES.txt +0 -0
  44. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw_sqlite3mc.egg-info/dependency_links.txt +0 -0
  45. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw_sqlite3mc.egg-info/entry_points.txt +0 -0
  46. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/apsw_sqlite3mc.egg-info/top_level.txt +0 -0
  47. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/pyproject.toml +0 -0
  48. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/setup.apsw +0 -0
  49. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/setup.cfg +0 -0
  50. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/_unicodedb.c +0 -0
  51. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/apsw.c +0 -0
  52. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/apsw.docstrings +0 -0
  53. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/argparse.c +0 -0
  54. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/async.c +0 -0
  55. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/backup.c +0 -0
  56. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/blob.c +0 -0
  57. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/carray.c +0 -0
  58. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/connection.c +0 -0
  59. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/constants.c +0 -0
  60. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/exceptions.c +0 -0
  61. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/faultinject.c +0 -0
  62. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/fileio_win32.c +0 -0
  63. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/fts.c +0 -0
  64. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/jsonb.c +0 -0
  65. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/pyutil.c +0 -0
  66. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/session.c +0 -0
  67. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/sqlite_debug.h +0 -0
  68. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/statementcache.c +0 -0
  69. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/stringconstants.c +0 -0
  70. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/testextension.c +0 -0
  71. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/traceback.c +0 -0
  72. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/unicode.c +0 -0
  73. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/util.c +0 -0
  74. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/vfs.c +0 -0
  75. {apsw_sqlite3mc-3.53.0.0 → apsw_sqlite3mc-3.53.1.0}/src/vtable.c +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apsw_sqlite3mc
3
- Version: 3.53.0.0
3
+ Version: 3.53.1.0
4
4
  Summary: SQLite3 Multiple Ciphers combined with Another Python SQLite Wrapper
5
5
  Home-page: https://github.com/utelle/apsw-sqlite3mc
6
6
  Author: Ulrich Telle
@@ -89,7 +89,7 @@ Setting the key on a new database is the only change needed to your code.
89
89
 
90
90
  >>> import apsw
91
91
  >>> print(apsw.mc_version)
92
- SQLite3 Multiple Ciphers 2.3.3
92
+ SQLite3 Multiple Ciphers 2.3.4
93
93
  >>> con = apsw.Connection("database.sqlite3")
94
94
  >>> con.pragma("key", "my secret passphrase")
95
95
  ok
@@ -59,7 +59,7 @@ Setting the key on a new database is the only change needed to your code.
59
59
 
60
60
  >>> import apsw
61
61
  >>> print(apsw.mc_version)
62
- SQLite3 Multiple Ciphers 2.3.3
62
+ SQLite3 Multiple Ciphers 2.3.4
63
63
  >>> con = apsw.Connection("database.sqlite3")
64
64
  >>> con.pragma("key", "my secret passphrase")
65
65
  ok
@@ -7500,13 +7500,13 @@ class AsyncCursor:
7500
7500
  * :attr:`Cursor.description_full`
7501
7501
  * :attr:`Cursor.description`"""
7502
7502
 
7503
- description: Awaitable[tuple[tuple[str, str, None, None, None, None, None], ...]]
7503
+ description: tuple[tuple[str, str, None, None, None, None, None], ...]
7504
7504
  """Based on the `DB-API cursor property
7505
7505
  <https://www.python.org/dev/peps/pep-0249/>`__, this returns the
7506
7506
  same as :meth:`get_description` but with 5 Nones appended because
7507
7507
  SQLite does not have the information."""
7508
7508
 
7509
- description_full: Awaitable[tuple[tuple[str, str, str, str, str], ...]]
7509
+ description_full: tuple[tuple[str, str, str, str, str], ...]
7510
7510
  """Only present if SQLITE_ENABLE_COLUMN_METADATA was defined at
7511
7511
  compile time.
7512
7512
 
@@ -7634,7 +7634,7 @@ class AsyncCursor:
7634
7634
  """Returns the :attr:`connection` this cursor is part of"""
7635
7635
  ...
7636
7636
 
7637
- async def get_description(self) -> tuple[tuple[str, str], ...]:
7637
+ def get_description(self) -> tuple[tuple[str, str], ...]:
7638
7638
  """If you are trying to get information about a table or view,
7639
7639
  then `pragma table_info <https://sqlite.org/pragma.html#pragma_table_info>`__
7640
7640
  is better. If you want to know up front what columns and other
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import contextlib
6
7
  import contextvars
7
8
  import logging
@@ -368,9 +369,6 @@ class AsyncIO:
368
369
  return await task
369
370
 
370
371
  def __init__(self, *, thread_name: str = "asyncio apsw background worker"):
371
- global asyncio
372
- import asyncio
373
-
374
372
  self.queue: queue.SimpleQueue[_CallTracker | None] = queue.SimpleQueue()
375
373
  self.loop = asyncio.get_running_loop()
376
374
  threading.Thread(name=thread_name, target=self.worker_thread_run).start()
@@ -651,8 +649,6 @@ def Auto() -> Trio | AsyncIO | AnyIO:
651
649
 
652
650
  if found is None and "asyncio" in sys.modules:
653
651
  try:
654
- import asyncio
655
-
656
652
  asyncio.get_running_loop()
657
653
  found = AsyncIO
658
654
  except:
@@ -180,7 +180,7 @@ class TypesConverterCursorFactory:
180
180
  """Registers a callable that converts from a SQLite value"""
181
181
  self.converters[name] = callable
182
182
 
183
- def __call__(self, connection: apsw.Connection) -> TypeConverterCursor:
183
+ def __call__(self, connection: apsw.Connection) -> TypesConverterCursorFactory.TypeConverterCursor:
184
184
  "Returns a new convertor :class:`cursor <apsw.Cursor>` for the `connection`"
185
185
  return TypesConverterCursorFactory.TypeConverterCursor(connection, self)
186
186
 
@@ -2224,7 +2224,7 @@ def get_column_names(row: Any) -> tuple[Sequence[str], VTColumnAccess]:
2224
2224
  * - Type
2225
2225
  - Access
2226
2226
  - Column names From
2227
- * - :external:func:`dataclasses.is_dataclass`
2227
+ * - :func:`dataclasses.is_dataclass`
2228
2228
  - :attr:`VTColumnAccess.By_Attr`
2229
2229
  - :func:`dataclasses.fields`
2230
2230
  * - :func:`isinstance <isinstance>`\(:class:`tuple`) and :func:`hasattr <hasattr>`\(:code:`"_fields"`) - eg :func:`~collections.namedtuple`
@@ -6058,6 +6058,7 @@ class APSW(unittest.TestCase):
6058
6058
  "get_description_full",
6059
6059
  "getdescription_dbapi",
6060
6060
  "bool",
6061
+ "next",
6061
6062
  ),
6062
6063
  "req": {
6063
6064
  "closed": "CHECK_CURSOR_CLOSED",
@@ -10,6 +10,7 @@ import functools
10
10
  import threading
11
11
  import unittest
12
12
  import tempfile
13
+ import random
13
14
  import sys
14
15
  import math
15
16
  import inspect
@@ -539,6 +540,209 @@ class Async(unittest.TestCase):
539
540
 
540
541
  self.assertEqual(values, list(range(min(NUM_ENTRIES, error))))
541
542
 
543
+ async def atestCursorStatementAttributes(self, fw):
544
+
545
+ # This is to verify #613 needing to keep iteration, prefetch,
546
+ # and description in sync with each other, not to mention all
547
+ # the other cursor statement related attributes. Multiple
548
+ # tables with varying number of columns and content are
549
+ # selected from in a single SQL string and async iteration
550
+ # compared to sync
551
+
552
+ # used to name the tables, columns, and types with languages
553
+ # from wikipedia.org front page
554
+ names = """one two three four five six seven eight nine ten
555
+ eleven twelve thirteen fourteen fifteen sixteen seventeen
556
+ eighteen nineteen twenty العربية Deutsch English Español فارسی
557
+ Français Italiano مصرى Nederlands日本語Polski Português
558
+ Sinugboanong Binisaya Svenska Українська Tiếng Việt Winaray中
559
+ 文Русский articles Afrikaans Shqip Asturianu Azərbaycanca
560
+ Български閩南語 Bân-lâm-gú বাংলা Беларуская Català Čeština
561
+ Cymraeg Dansk Eesti Ελληνικά Esperanto Euskara Galego 한국어
562
+ Հայերեն हिन्दी Hrvatski Bahasa Indonesia עברית ქართული Ladin
563
+ Latina Latviešu Lietuvių Magyar Македонски Malagasy मराठी
564
+ Bahasa Melayu Bahaso Minangkabau မြန်မာဘာသာ Norskbokmålnynorsk
565
+ Нохчийн Oʻzbekcha Ўзбекча Қазақша Qazaqşa قازاقشا Română
566
+ Simple English Slovenčina Slovenščina Српски Srpski
567
+ Srpskohrvatski Српскохрватски Suomi Kiswahili தமிழ் Татарча
568
+ Tatarça తెలుగు ภาษาไทย Тоҷикӣ تۆرکجه Türkçe اردو粵語Bahsa Acèh
569
+ Alemannisch አማርኛ Aragonés Արեւմտահայերէն Bahasa Hulontalo Basa
570
+ Bali Bahasa Banjar Basa Banyumasan Башҡортса Беларуская
571
+ тарашкевіца Bikol Central বিষ্ণুপ্রিয়া মণিপুরী Boarisch
572
+ Bosanski Brezhoneg Чӑвашла Dagbanli الدارجة Diné Bizaad
573
+ Emigliàn–Rumagnòl Fiji Hindi Føroyskt Frysk Fulfulde Gaeilge
574
+ Gàidhlig گیلکی ગુજરાતી Hak-kâ-ngî 客家語Hausa Hornjoserbsce
575
+ Ido Igbo Ilokano Interlingua Interlingue Ирон Íslenska Jawa
576
+ ಕನ್ನಡ Kapampangan ភាសាខ្មែរ Kotava Kreyòl Ayisyen Kurdî كوردی
577
+ کوردیی ناوەندی Кыргызча Кырык мары Lëtzebuergesch Lìgure
578
+ Limburgs Lombard मैथिली മലയാളം მარგალური مازِرونی
579
+ Mìng-dĕ̤ng-ngṳ̄ 閩東語Монгол Napulitano नेपाल भाषा Nordfriisk
580
+ Occitan Олык марий ଓଡି଼ଆ অসমীযা় ਪੰਜਾਬੀ پنجابی شاہ مکھی پښتو
581
+ Piemontèis Plattdüütsch Qaraqalpaqsha Qırımtatarca Runa Simi
582
+ Русиньскый संस्कृतम् ᱥᱟᱱᱛᱟᱲᱤ سرائیکی Саха Тыла Scots ChiShona
583
+ Sicilianu සිංහල سنڌي Ślůnski Soomaaliga Basa Sunda Taclḥit
584
+ Tagalog ၽႃႇသႃႇတႆး ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ tolışi chiTumbuka Basa Ugi
585
+ Vèneto Volapük Walon文言吴语ייִדיש """.split()
586
+
587
+ con = apsw.Connection("")
588
+ acon = await apsw.Connection.as_async("")
589
+
590
+ has_full = hasattr(apsw.Cursor, "description_full")
591
+
592
+ try:
593
+ apsw.allow_missing_dict_bindings(True)
594
+
595
+ tables = []
596
+
597
+ table_columns = {}
598
+
599
+ for table in random.choices(names, k=6):
600
+ # choices can return duplicates
601
+ if table in tables:
602
+ continue
603
+ tables.append(table)
604
+ sql = ""
605
+ sql += f'create table "{table}"('
606
+ columns = list(set(random.choices(names, k=random.randint(1, 10))))
607
+ table_columns[table] = columns
608
+
609
+ sql += ",".join(
610
+ f'"{name}" "{coltype}"' for (name, coltype) in zip(columns, random.choices(names, k=len(columns)))
611
+ )
612
+ sql += ");"
613
+
614
+ con.execute(sql)
615
+ await acon.execute(sql)
616
+
617
+ insert = f'insert into "{table}" values(' + ",".join("?" * len(columns)) + ")"
618
+
619
+ async with acon:
620
+ with con:
621
+ for row in range(random.randint(0, 100)):
622
+ con.execute(insert, [row] * len(columns))
623
+ await acon.execute(insert, [row] * len(columns))
624
+
625
+ sql = []
626
+ emsql = []
627
+ emvalues = []
628
+
629
+ stuff = "abcdefghijklmnopqrstuvwxyz"
630
+
631
+ for i in range(12):
632
+ sql.append(f'SELECT * FROM "{random.choice(tables)}" LIMIT {random.randint(0, 10)};')
633
+ table = random.choice(tables)
634
+ one = "".join(random.choices(stuff, k=10))
635
+ two = "".join(random.choices(stuff, k=10))
636
+ emsql.append(f'SELECT * FROM "{table}" WHERE coalesce(${one},:{two}, 3) is not null LIMIT :limit ;')
637
+ emvalues.append({"limit": random.randint(0, 10), one: 77, two: 94})
638
+ if i in {1, 3, 7, 11, 17}:
639
+ for q in (
640
+ "/* no vdbe for this */ ;",
641
+ "explain query plan select * from sqlite_schema;",
642
+ "create table if not exists orange(a,c);",
643
+ ):
644
+ sql.append(q)
645
+ emsql.append(q)
646
+
647
+ embindings = emvalues[:]
648
+ random.shuffle(embindings)
649
+
650
+ async_attrs = "bindings_names expanded_sql sql".split()
651
+ sync_attrs = "bindings_count has_vdbe is_explain is_readonly description".split() + (
652
+ ["description_full"] if has_full else []
653
+ )
654
+
655
+ sync_cur = con.cursor()
656
+ async_cur = acon.cursor()
657
+ self.assertRaises(apsw.ExecutionCompleteError, sync_cur.get_description)
658
+ self.assertRaises(apsw.ExecutionCompleteError, async_cur.get_description)
659
+ for a in async_attrs:
660
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, sync_cur, a)
661
+ with self.assertRaises(apsw.ExecutionCompleteError):
662
+ await getattr(async_cur, a)
663
+ for a in sync_attrs:
664
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, sync_cur, a)
665
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, async_cur, a)
666
+
667
+ for prefetch in (1, 3, 11, 64):
668
+ apsw.async_cursor_prefetch.set(prefetch)
669
+ random.shuffle(sql)
670
+ # regular execute mode
671
+ sync_cur = con.execute("".join(sql))
672
+ async_cur = await acon.execute("".join(sql))
673
+
674
+ async for row in async_cur:
675
+ self.assertEqual(row, next(sync_cur))
676
+ self.assertEqual(sync_cur.get_description(), async_cur.get_description())
677
+ for a in async_attrs:
678
+ self.assertEqual(getattr(sync_cur, a), await getattr(async_cur, a))
679
+ for a in sync_attrs:
680
+ self.assertEqual(getattr(sync_cur, a), getattr(async_cur, a))
681
+
682
+ # check raises complete
683
+ self.assertRaises(StopIteration, next, sync_cur)
684
+ with self.assertRaises(StopAsyncIteration):
685
+ await anext(async_cur)
686
+
687
+ self.assertRaises(apsw.ExecutionCompleteError, sync_cur.get_description)
688
+ self.assertRaises(apsw.ExecutionCompleteError, async_cur.get_description)
689
+ for a in async_attrs:
690
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, sync_cur, a)
691
+ with self.assertRaises(apsw.ExecutionCompleteError):
692
+ await getattr(async_cur, a)
693
+ for a in sync_attrs:
694
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, sync_cur, a)
695
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, async_cur, a)
696
+
697
+ # errors
698
+ error_sql = "select 3; select syntax error"
699
+ sync_cur = con.execute(error_sql)
700
+ async_cur = aiter(await acon.execute(error_sql))
701
+
702
+ next(sync_cur)
703
+ await anext(async_cur)
704
+
705
+ self.assertRaises(apsw.SQLError, next, sync_cur)
706
+ with self.assertRaises(apsw.SQLError):
707
+ await anext(async_cur)
708
+
709
+ self.assertRaises(StopIteration, next, sync_cur)
710
+ with self.assertRaises(StopAsyncIteration):
711
+ await anext(async_cur)
712
+
713
+ # executemany mode
714
+ random.shuffle(emsql)
715
+
716
+ sync_cur = con.executemany("".join(emsql), embindings)
717
+ async_cur = await acon.executemany("".join(emsql), embindings)
718
+ async for row in async_cur:
719
+ self.assertEqual(row, next(sync_cur))
720
+ self.assertEqual(sync_cur.get_description(), async_cur.get_description())
721
+ for a in async_attrs:
722
+ self.assertEqual(getattr(sync_cur, a), await getattr(async_cur, a))
723
+ for a in sync_attrs:
724
+ self.assertEqual(getattr(sync_cur, a), getattr(async_cur, a))
725
+
726
+ # check raises complete
727
+ self.assertRaises(StopIteration, next, sync_cur)
728
+ with self.assertRaises(StopAsyncIteration):
729
+ await anext(async_cur)
730
+
731
+ self.assertRaises(apsw.ExecutionCompleteError, sync_cur.get_description)
732
+ self.assertRaises(apsw.ExecutionCompleteError, async_cur.get_description)
733
+ for a in async_attrs:
734
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, sync_cur, a)
735
+ with self.assertRaises(apsw.ExecutionCompleteError):
736
+ await getattr(async_cur, a)
737
+ for a in sync_attrs:
738
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, sync_cur, a)
739
+ self.assertRaises(apsw.ExecutionCompleteError, getattr, async_cur, a)
740
+
741
+ finally:
742
+ apsw.allow_missing_dict_bindings(False)
743
+ con.close(True)
744
+ acon.close(True)
745
+
542
746
  async def atestConfigure(self, fw):
543
747
  auto = apsw.aio.Auto()
544
748
  auto.close()
@@ -1109,12 +1109,12 @@ class JSONB(unittest.TestCase):
1109
1109
 
1110
1110
  def testRandomJSON(self):
1111
1111
  "sqlite randomjson extension if present"
1112
- if not os.path.exists("randomjson.so"):
1112
+ import apsw.sqlite_extra
1113
+ try:
1114
+ apsw.sqlite_extra.load(self.db, "randomjson")
1115
+ except apsw.sqlite_extra.NotAvailable:
1113
1116
  return
1114
1117
 
1115
- self.db.enable_load_extension(True)
1116
- self.db.load_extension("./randomjson")
1117
-
1118
1118
  for seed in range(2000, 3000):
1119
1119
  # we round trip the json through as many combinations of SQLite, Python and APSW
1120
1120
  # encoders / decoders as possible
@@ -141,6 +141,7 @@ class MultipleCiphers(unittest.TestCase):
141
141
  for config in (
142
142
  {
143
143
  "plaintext_header_size": 64,
144
+ "cipher_salt": "B1349F791FE98DDD1648406C5554BD1F",
144
145
  "cipher": "sqlcipher",
145
146
  "key": "one",
146
147
  },
@@ -286,6 +287,9 @@ def apply_encryption(db, **kwargs):
286
287
  # then anything with legacy in the name
287
288
  if "legacy" in pragma:
288
289
  return 3
290
+ # explicit cipher salt
291
+ if pragma == "cipher_salt":
292
+ return 99
289
293
  # all except keys
290
294
  if pragma not in {"key", "hexkey", "rekey", "hexrekey"}:
291
295
  return 3
@@ -299,7 +303,7 @@ def apply_encryption(db, **kwargs):
299
303
  for pragma, value in sorted(kwargs.items(), key=pragma_order):
300
304
  # if the pragma was understood and in range we get the value
301
305
  # back, while key related ones return 'ok'
302
- expected = "ok" if pragma_order((pragma, value)) == 100 else str(value)
306
+ expected = "ok" if pragma_order((pragma, value)) >= 99 else str(value)
303
307
  if db.pragma(pragma, value) != expected:
304
308
  raise ValueError(f"Failed to configure {pragma=}")
305
309
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apsw-sqlite3mc
3
- Version: 3.53.0.0
3
+ Version: 3.53.1.0
4
4
  Summary: SQLite3 Multiple Ciphers combined with Another Python SQLite Wrapper
5
5
  Home-page: https://github.com/utelle/apsw-sqlite3mc
6
6
  Author: Ulrich Telle
@@ -89,7 +89,7 @@ Setting the key on a new database is the only change needed to your code.
89
89
 
90
90
  >>> import apsw
91
91
  >>> print(apsw.mc_version)
92
- SQLite3 Multiple Ciphers 2.3.3
92
+ SQLite3 Multiple Ciphers 2.3.4
93
93
  >>> con = apsw.Connection("database.sqlite3")
94
94
  >>> con.pragma("key", "my secret passphrase")
95
95
  ok
@@ -9,7 +9,7 @@
9
9
  # way.
10
10
 
11
11
  # These are used too compile sqlite extra
12
-
12
+ https://sqlite.org/vec1/zip/vec1-20260505104119-eb38e10fef.zip 149803 b37509b229a393a387f3f4f352529dcf10cc3ebdbb2c75fa062763d4fdd0953b ceb3aa3699c378872e88b06b237e94eec9e545ffc1abe7388f521a107ec50699
13
13
  https://sqlite.org/vec1/zip/vec1-20260409204746-4b73767df0.zip 119674 952af14601531b527a497df81d038a2693c1a39dba2d76ed798c3e5ba3dd7c22 55e2d7e05da20b7eb4abbe77688bdff4662a6f4f248f6710e7d2b605d3c90107
14
14
  https://sqlite.org/vec1/zip/vec1-20260330201854-5b7c141049.zip 107146 099acc5f1eee719cae16d4843e904bba432cda9ce43d1cefb756f6566462d9f5 1c3e4264a416d68209ea37139c7e0ad42fcee4f66c021c8f495cce9882bc71e3
15
15
  https://sqlite.org/vec1/zip/vec1-20260306155250-d070184523.zip 75980 6a58eebbe1e4679645c80084fdfc44f2902e19cde95da5af932bb51bfdc7d02d 77785c8f4e0a9672dded6794861a2ca9d5a5a8dda43300b183690cd84149bf35
@@ -20,6 +20,8 @@ https://sqlite.org/sqlar/zip/sqlar-src-20180107193712-4824e73896.zip 1980460 63b
20
20
  https://www.zlib.net/zlib132.zip 1616754 e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 6b35b981f73425dc5f142cbee457f078d42240c022c804982849b962f1d1e2c6
21
21
 
22
22
  # SQLite releases
23
+ https://sqlite.org/2026/sqlite-autoconf-3530100.tar.gz 3275272 83e6b2020a034e9a7ad4a72feea59e1ad52f162e09cbd26735a3ffb98359fc4f 36ca143645cf76997d07b66e9244c636b8ccdec64a1d50558259c4e415e6558b
24
+ https://sqlite.org/2026/sqlite-src-3530100.zip 14515734 1b2b5755d9064c4d5d1b0bf5307b48b089963e291c40cc7351318aa1b61c460e 27cfc9264b2188fd17f811a8c03424eb65391c2ef9874cbfc860ea25f4322363
23
25
  https://sqlite.org/2026/sqlite-autoconf-3530000.tar.gz 3274709 851e9b38192fe2ceaa65e0baa665e7fa06230c3d9bd1a6a9662d02380d73365a 60c4b08c6729761e488d185e0d52411da10b14c72b53ada6936dc5eea225cefe
24
26
  https://sqlite.org/2026/sqlite-src-3530000.zip 14513779 fbc30cdbfcfa42c78fe7bddd3fd37ab8995369a31d39097a5d0633296c0b6e65 4ffbd00ba8db1e1172dbc69a5203a2c185556a32543e58585ba3713abf676fe5
25
27
 
@@ -306,7 +306,7 @@ class fetch(Command):
306
306
  for desc, url, replace in (
307
307
  (
308
308
  "experimental vec1 extension source",
309
- "https://sqlite.org/vec1/zip/vec1-20260409204746-4b73767df0.zip",
309
+ "https://sqlite.org/vec1/zip/vec1-20260505104119-eb38e10fef.zip",
310
310
  "sqlite3/vec1",
311
311
  ),
312
312
  (