tina4-python 3.13.44__tar.gz → 3.13.46__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 (158) hide show
  1. {tina4_python-3.13.44 → tina4_python-3.13.46}/PKG-INFO +1 -1
  2. {tina4_python-3.13.44 → tina4_python-3.13.46}/pyproject.toml +1 -1
  3. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/__init__.py +1 -1
  4. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/mssql.py +8 -2
  5. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/sqlite.py +35 -15
  6. {tina4_python-3.13.44 → tina4_python-3.13.46}/.gitignore +0 -0
  7. {tina4_python-3.13.44 → tina4_python-3.13.46}/README.md +0 -0
  8. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/CLAUDE.md +0 -0
  9. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/HtmlElement.py +0 -0
  10. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/Testing.py +0 -0
  11. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/ai/__init__.py +0 -0
  12. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/api/__init__.py +0 -0
  13. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/auth/__init__.py +0 -0
  14. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/cache/__init__.py +0 -0
  15. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/cli/__init__.py +0 -0
  16. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/container/__init__.py +0 -0
  17. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/__init__.py +0 -0
  18. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/cache.py +0 -0
  19. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/constants.py +0 -0
  20. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/events.py +0 -0
  21. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/middleware.py +0 -0
  22. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/rate_limiter.py +0 -0
  23. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/request.py +0 -0
  24. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/response.py +0 -0
  25. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/router.py +0 -0
  26. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/core/server.py +0 -0
  27. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/crud/__init__.py +0 -0
  28. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/__init__.py +0 -0
  29. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/adapter.py +0 -0
  30. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/connection.py +0 -0
  31. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/firebird.py +0 -0
  32. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/mongodb.py +0 -0
  33. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/mysql.py +0 -0
  34. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/odbc.py +0 -0
  35. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/database/postgres.py +0 -0
  36. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/debug/__init__.py +0 -0
  37. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/debug/error_overlay.py +0 -0
  38. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/dev_admin/__init__.py +0 -0
  39. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/dev_admin/metrics.py +0 -0
  40. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/dev_admin/plan.py +0 -0
  41. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/dev_admin/project_index.py +0 -0
  42. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/docs.py +0 -0
  43. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/docstore/__init__.py +0 -0
  44. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/dotenv/__init__.py +0 -0
  45. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/env.py +0 -0
  46. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/frond/FROND.md +0 -0
  47. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/frond/__init__.py +0 -0
  48. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/frond/engine.py +0 -0
  49. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/auth/meta.json +0 -0
  50. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  51. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/database/meta.json +0 -0
  52. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  53. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/error-overlay/meta.json +0 -0
  54. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  55. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/orm/meta.json +0 -0
  56. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  57. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  58. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/queue/meta.json +0 -0
  59. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  60. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/rest-api/meta.json +0 -0
  61. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  62. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/templates/meta.json +0 -0
  63. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  64. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  65. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/graphql/__init__.py +0 -0
  66. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/i18n/__init__.py +0 -0
  67. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/mcp/__init__.py +0 -0
  68. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/mcp/protocol.py +0 -0
  69. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/mcp/tools.py +0 -0
  70. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/messenger/__init__.py +0 -0
  71. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/migration/__init__.py +0 -0
  72. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/migration/runner.py +0 -0
  73. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/orm/__init__.py +0 -0
  74. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/orm/fields.py +0 -0
  75. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/orm/model.py +0 -0
  76. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/__feedback/widget.js +0 -0
  77. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/css/tina4.css +0 -0
  78. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/css/tina4.min.css +0 -0
  79. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/favicon.ico +0 -0
  80. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/images/logo.svg +0 -0
  81. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  82. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/js/frond.js +0 -0
  83. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/js/frond.min.js +0 -0
  84. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/js/tina4-dev-admin.js +0 -0
  85. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  86. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/js/tina4.min.js +0 -0
  87. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/js/tina4js.min.js +0 -0
  88. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/swagger/index.html +0 -0
  89. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  90. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/query_builder/__init__.py +0 -0
  91. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue/__init__.py +0 -0
  92. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue/job.py +0 -0
  93. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue/kafka_backend.py +0 -0
  94. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue/lite_backend.py +0 -0
  95. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue/mongo_backend.py +0 -0
  96. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue/rabbitmq_backend.py +0 -0
  97. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue_backends/__init__.py +0 -0
  98. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue_backends/kafka_backend.py +0 -0
  99. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue_backends/mongo_backend.py +0 -0
  100. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  101. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/__init__.py +0 -0
  102. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  103. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_badges.scss +0 -0
  104. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  105. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_cards.scss +0 -0
  106. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_forms.scss +0 -0
  107. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_grid.scss +0 -0
  108. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_modals.scss +0 -0
  109. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_nav.scss +0 -0
  110. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_reset.scss +0 -0
  111. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_tables.scss +0 -0
  112. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_typography.scss +0 -0
  113. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  114. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/_variables.scss +0 -0
  115. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/base.scss +0 -0
  116. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/colors.scss +0 -0
  117. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/scss/tina4css/tina4.scss +0 -0
  118. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/seeder/__init__.py +0 -0
  119. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/service/__init__.py +0 -0
  120. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/session/__init__.py +0 -0
  121. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/session_handlers/__init__.py +0 -0
  122. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  123. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/session_handlers/redis_handler.py +0 -0
  124. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/session_handlers/valkey_handler.py +0 -0
  125. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/swagger/__init__.py +0 -0
  126. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/components/crud.twig +0 -0
  127. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  128. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  129. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/docker/python/Dockerfile +0 -0
  130. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  131. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/302.twig +0 -0
  132. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/401.twig +0 -0
  133. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/403.twig +0 -0
  134. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/404.twig +0 -0
  135. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/500.twig +0 -0
  136. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/502.twig +0 -0
  137. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/503.twig +0 -0
  138. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/errors/base.twig +0 -0
  139. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/frontend/README.md +0 -0
  140. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/templates/readme.md +0 -0
  141. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/test/__init__.py +0 -0
  142. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/test_client/__init__.py +0 -0
  143. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  144. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  145. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  146. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  147. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  148. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  149. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  150. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  151. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  152. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  153. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  154. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  155. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/validator/__init__.py +0 -0
  156. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/websocket/__init__.py +0 -0
  157. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/websocket/backplane.py +0 -0
  158. {tina4_python-3.13.44 → tina4_python-3.13.46}/tina4_python/wsdl/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 3.13.44
3
+ Version: 3.13.46
4
4
  Summary: Tina4 Python v3 — Zero-dependency, lightweight web framework
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tina4-python"
3
- version = "3.13.44"
3
+ version = "3.13.46"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -8,7 +8,7 @@ Tina4 Python v3.0 — Zero-dependency, lightweight web framework.
8
8
 
9
9
  One import, everything works.
10
10
  """
11
- __version__ = "3.13.44"
11
+ __version__ = "3.13.46"
12
12
 
13
13
  # ── Route decorators ──
14
14
  from tina4_python.core.router import ( # noqa: E402, F401
@@ -64,6 +64,14 @@ class MSSQLAdapter(DatabaseAdapter):
64
64
  cursor = self._conn.cursor(as_dict=True)
65
65
  cursor.execute(sql, tuple(params) if params else ())
66
66
 
67
+ # Capture the affected-row count from the MAIN statement NOW, before any
68
+ # follow-up SELECT (SCOPE_IDENTITY / RETURNING fetch) runs on the SAME
69
+ # cursor and overwrites cursor.rowcount. Reading it at the end reflected
70
+ # the SCOPE_IDENTITY SELECT instead of the INSERT, so every INSERT
71
+ # reported affected_rows=0 (a batch insert summed to 0 even though the
72
+ # rows landed — surfaced by the live MySQL/MSSQL batch test).
73
+ affected = cursor.rowcount if cursor.rowcount is not None and cursor.rowcount >= 0 else 0
74
+
67
75
  records = []
68
76
  last_id = None
69
77
 
@@ -89,8 +97,6 @@ class MSSQLAdapter(DatabaseAdapter):
89
97
  if row:
90
98
  records = [dict(row)]
91
99
 
92
- affected = cursor.rowcount if cursor.rowcount >= 0 else 0
93
-
94
100
  if not self._in_transaction and self.autocommit:
95
101
  self._conn.commit()
96
102
 
@@ -93,23 +93,43 @@ class SQLiteAdapter(DatabaseAdapter):
93
93
  )
94
94
 
95
95
  def execute_many(self, sql: str, params_list: list[list] = None) -> DatabaseResult:
96
- """Optimized batch execute using SQLite's executemany."""
96
+ """Optimized batch execute using SQLite's executemany — ATOMIC (one txn).
97
+
98
+ The batch runs inside one transaction so a bad row mid-batch rolls the
99
+ WHOLE batch back (all-or-nothing) instead of leaving the rows applied
100
+ before the failure in an open, uncommitted transaction. Mirrors the base
101
+ DatabaseAdapter.execute_many owns-transaction guard and the other engines.
102
+ """
97
103
  sql = self._translate_sql(sql)
98
104
  rows = params_list or []
99
- self._conn.executemany(sql, rows)
100
-
101
- # cursor.rowcount / cursor.lastrowid are unreliable after executemany()
102
- # in sqlite3 (rowcount can come back 0 or -1, lastrowid is not set) and
103
- # were non-deterministic across pooled connections. The batch is
104
- # all-or-raise, so every supplied row was applied; the last inserted id
105
- # is read deterministically from last_insert_rowid() on this connection.
106
- affected = len(rows)
107
- last_row = self._conn.execute("SELECT last_insert_rowid()").fetchone()
108
- last_id = last_row[0] if last_row and last_row[0] else None
109
-
110
- if not self._in_transaction and self.autocommit:
111
- if self._conn.in_transaction:
112
- self._conn.execute("COMMIT")
105
+
106
+ # Own (and commit/rollback) the batch transaction only for a standalone
107
+ # write in autocommit mode never inside a caller's explicit transaction.
108
+ standalone = self.autocommit and not self._in_transaction
109
+ if standalone and not self._conn.in_transaction:
110
+ self._conn.execute("BEGIN")
111
+
112
+ try:
113
+ self._conn.executemany(sql, rows)
114
+ # cursor.rowcount / cursor.lastrowid are unreliable after executemany()
115
+ # in sqlite3 (rowcount can come back 0 or -1, lastrowid is not set).
116
+ # The batch is all-or-nothing, so every supplied row was applied; read
117
+ # the last inserted id deterministically from last_insert_rowid().
118
+ affected = len(rows)
119
+ last_row = self._conn.execute("SELECT last_insert_rowid()").fetchone()
120
+ last_id = last_row[0] if last_row and last_row[0] else None
121
+ except Exception:
122
+ # Roll the partial batch back so nothing is left half-applied, then
123
+ # FAIL LOUD (re-raise) — parity with execute() and the other engines.
124
+ if standalone and self._conn.in_transaction:
125
+ try:
126
+ self._conn.execute("ROLLBACK")
127
+ except Exception:
128
+ pass
129
+ raise
130
+
131
+ if standalone and self._conn.in_transaction:
132
+ self._conn.execute("COMMIT")
113
133
 
114
134
  return DatabaseResult(
115
135
  affected_rows=affected,
File without changes