tina4-python 3.10.18__tar.gz → 3.10.23__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 (139) hide show
  1. {tina4_python-3.10.18 → tina4_python-3.10.23}/PKG-INFO +1 -1
  2. {tina4_python-3.10.18 → tina4_python-3.10.23}/pyproject.toml +1 -1
  3. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/__init__.py +1 -1
  4. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/connection.py +93 -17
  5. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/frond/engine.py +226 -70
  6. {tina4_python-3.10.18 → tina4_python-3.10.23}/.gitignore +0 -0
  7. {tina4_python-3.10.18 → tina4_python-3.10.23}/README.md +0 -0
  8. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/CLAUDE.md +0 -0
  9. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/HtmlElement.py +0 -0
  10. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/Testing.py +0 -0
  11. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/ai/__init__.py +0 -0
  12. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/api/__init__.py +0 -0
  13. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/auth/__init__.py +0 -0
  14. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/cache/__init__.py +0 -0
  15. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/cli/__init__.py +0 -0
  16. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/container/__init__.py +0 -0
  17. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/__init__.py +0 -0
  18. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/cache.py +0 -0
  19. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/constants.py +0 -0
  20. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/events.py +0 -0
  21. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/middleware.py +0 -0
  22. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/request.py +0 -0
  23. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/response.py +0 -0
  24. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/router.py +0 -0
  25. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/core/server.py +0 -0
  26. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/crud/__init__.py +0 -0
  27. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/__init__.py +0 -0
  28. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/adapter.py +0 -0
  29. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/firebird.py +0 -0
  30. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/mssql.py +0 -0
  31. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/mysql.py +0 -0
  32. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/odbc.py +0 -0
  33. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/postgres.py +0 -0
  34. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/database/sqlite.py +0 -0
  35. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/debug/__init__.py +0 -0
  36. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/debug/error_overlay.py +0 -0
  37. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/dev_admin/__init__.py +0 -0
  38. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/dev_reload.py +0 -0
  39. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/dotenv/__init__.py +0 -0
  40. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/frond/FROND.md +0 -0
  41. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/frond/__init__.py +0 -0
  42. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/auth/meta.json +0 -0
  43. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  44. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/database/meta.json +0 -0
  45. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  46. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/error-overlay/meta.json +0 -0
  47. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  48. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/orm/meta.json +0 -0
  49. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  50. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  51. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/queue/meta.json +0 -0
  52. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  53. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/rest-api/meta.json +0 -0
  54. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  55. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/templates/meta.json +0 -0
  56. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  57. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  58. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/graphql/__init__.py +0 -0
  59. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/i18n/__init__.py +0 -0
  60. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/messenger/__init__.py +0 -0
  61. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/migration/__init__.py +0 -0
  62. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/migration/runner.py +0 -0
  63. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/orm/__init__.py +0 -0
  64. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/orm/fields.py +0 -0
  65. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/orm/model.py +0 -0
  66. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/css/tina4.css +0 -0
  67. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/css/tina4.min.css +0 -0
  68. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/favicon.ico +0 -0
  69. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/images/logo.svg +0 -0
  70. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  71. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/js/frond.min.js +0 -0
  72. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  73. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/js/tina4.min.js +0 -0
  74. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/js/tina4js.min.js +0 -0
  75. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/swagger/index.html +0 -0
  76. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  77. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/query_builder/__init__.py +0 -0
  78. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/queue/__init__.py +0 -0
  79. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/queue_backends/__init__.py +0 -0
  80. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/queue_backends/kafka_backend.py +0 -0
  81. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/queue_backends/mongo_backend.py +0 -0
  82. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  83. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/__init__.py +0 -0
  84. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  85. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_badges.scss +0 -0
  86. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  87. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_cards.scss +0 -0
  88. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_forms.scss +0 -0
  89. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_grid.scss +0 -0
  90. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_modals.scss +0 -0
  91. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_nav.scss +0 -0
  92. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_reset.scss +0 -0
  93. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_tables.scss +0 -0
  94. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_typography.scss +0 -0
  95. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  96. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/_variables.scss +0 -0
  97. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/base.scss +0 -0
  98. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/colors.scss +0 -0
  99. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/scss/tina4css/tina4.scss +0 -0
  100. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/seeder/__init__.py +0 -0
  101. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/service/__init__.py +0 -0
  102. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/session/__init__.py +0 -0
  103. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/session_handlers/__init__.py +0 -0
  104. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  105. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/session_handlers/redis_handler.py +0 -0
  106. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/session_handlers/valkey_handler.py +0 -0
  107. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/swagger/__init__.py +0 -0
  108. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/components/crud.twig +0 -0
  109. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  110. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  111. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/docker/python/Dockerfile +0 -0
  112. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  113. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/302.twig +0 -0
  114. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/401.twig +0 -0
  115. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/403.twig +0 -0
  116. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/404.twig +0 -0
  117. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/500.twig +0 -0
  118. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/502.twig +0 -0
  119. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/503.twig +0 -0
  120. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/errors/base.twig +0 -0
  121. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/frontend/README.md +0 -0
  122. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/templates/readme.md +0 -0
  123. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/test_client/__init__.py +0 -0
  124. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  125. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  126. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  127. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  128. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  129. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  130. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  131. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  132. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  133. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  134. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  135. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  136. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/validator/__init__.py +0 -0
  137. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/websocket/__init__.py +0 -0
  138. {tina4_python-3.10.18 → tina4_python-3.10.23}/tina4_python/websocket/backplane.py +0 -0
  139. {tina4_python-3.10.18 → tina4_python-3.10.23}/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.10.18
3
+ Version: 3.10.23
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.10.18"
3
+ version = "3.10.23"
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.9.2"
11
+ __version__ = "3.10.23"
12
12
 
13
13
  # ── HTTP Constants ──
14
14
  from tina4_python.core.constants import ( # noqa: E402, F401
@@ -383,19 +383,86 @@ class Database:
383
383
  elif self._adapter is not None:
384
384
  self._adapter.autocommit = value
385
385
 
386
+ def _ensure_sequence_table(self):
387
+ """Create the tina4_sequences table if it doesn't exist."""
388
+ if not self.table_exists("tina4_sequences"):
389
+ engine = self.get_database_type()
390
+ if engine == "mssql":
391
+ self.execute(
392
+ "CREATE TABLE tina4_sequences ("
393
+ "seq_name VARCHAR(200) NOT NULL PRIMARY KEY, "
394
+ "current_value INTEGER NOT NULL DEFAULT 0)"
395
+ )
396
+ else:
397
+ self.execute(
398
+ "CREATE TABLE IF NOT EXISTS tina4_sequences ("
399
+ "seq_name VARCHAR(200) NOT NULL PRIMARY KEY, "
400
+ "current_value INTEGER NOT NULL DEFAULT 0)"
401
+ )
402
+ self.commit()
403
+
404
+ def _sequence_next(self, seq_name: str, table: str = None, pk_column: str = "id") -> int:
405
+ """Atomically increment and return the next value from the sequence table.
406
+
407
+ If the sequence row doesn't exist yet, seeds it from MAX(pk_column)
408
+ of the given table (or 0 if the table is empty/missing).
409
+ """
410
+ self._ensure_sequence_table()
411
+
412
+ # Check if the sequence row exists
413
+ row = self.fetch_one(
414
+ "SELECT current_value FROM tina4_sequences WHERE seq_name = ?",
415
+ [seq_name]
416
+ )
417
+
418
+ if row is None:
419
+ # Seed from current MAX
420
+ seed_value = 0
421
+ if table:
422
+ try:
423
+ max_row = self.fetch_one(
424
+ f"SELECT MAX({pk_column}) AS max_id FROM {table}"
425
+ )
426
+ if max_row and max_row.get("max_id") is not None:
427
+ seed_value = int(max_row["max_id"])
428
+ except Exception:
429
+ pass # Table doesn't exist — start at 0
430
+
431
+ self.execute(
432
+ "INSERT INTO tina4_sequences (seq_name, current_value) VALUES (?, ?)",
433
+ [seq_name, seed_value]
434
+ )
435
+ self.commit()
436
+
437
+ # Atomic increment + read
438
+ self.execute(
439
+ "UPDATE tina4_sequences SET current_value = current_value + 1 "
440
+ "WHERE seq_name = ?",
441
+ [seq_name]
442
+ )
443
+ self.commit()
444
+
445
+ row = self.fetch_one(
446
+ "SELECT current_value FROM tina4_sequences WHERE seq_name = ?",
447
+ [seq_name]
448
+ )
449
+ return int(row["current_value"]) if row else 1
450
+
386
451
  def get_next_id(self, table: str, pk_column: str = "id", generator_name: str = None) -> int:
387
452
  """Get the next available ID for a table.
388
453
 
389
454
  Engine-specific strategies:
390
- - Firebird: uses GEN_ID(generator, 1) to increment and return
391
- - PostgreSQL: uses nextval(sequence) if sequence exists
392
- - SQLite/MySQL/MSSQL: SELECT MAX(pk) + 1 (fallback)
455
+ - Firebird: uses GEN_ID(generator, 1) atomic increment
456
+ - PostgreSQL: uses nextval(sequence) atomic increment;
457
+ auto-creates sequence if missing
458
+ - SQLite/MySQL/MSSQL: uses tina4_sequences table with atomic
459
+ UPDATE + SELECT (race-safe, replaces old MAX+1)
393
460
 
394
461
  Args:
395
462
  table: Table name.
396
463
  pk_column: Primary key column name (default: "id").
397
- generator_name: Firebird generator or PostgreSQL sequence name.
398
- Auto-detected if not provided.
464
+ generator_name: Firebird generator, PostgreSQL sequence name,
465
+ or sequence table key override.
399
466
 
400
467
  Returns:
401
468
  The next integer ID.
@@ -413,24 +480,33 @@ class Database:
413
480
  row = self.fetch_one(
414
481
  f"SELECT GEN_ID({gen_name}, 1) AS next_id FROM RDB$DATABASE"
415
482
  )
416
- return row["next_id"] if row else 1
483
+ return int(row["next_id"]) if row else 1
417
484
 
418
- elif engine == "postgresql":
485
+ if engine == "postgresql":
419
486
  seq_name = generator_name or f"{table}_{pk_column}_seq"
420
487
  try:
421
488
  row = self.fetch_one(f"SELECT nextval('{seq_name}') AS next_id")
422
- return row["next_id"] if row else 1
489
+ if row and row.get("next_id") is not None:
490
+ return int(row["next_id"])
423
491
  except Exception:
424
- pass # Sequence doesn't exist, fall through to MAX
492
+ pass # Sequence doesn't exist
425
493
 
426
- # Fallback: MAX(pk) + 1
427
- try:
428
- row = self.fetch_one(
429
- f"SELECT MAX({pk_column}) AS max_id FROM {table}"
430
- )
431
- return (row["max_id"] or 0) + 1 if row else 1
432
- except Exception:
433
- return 1
494
+ # Auto-create sequence seeded from MAX
495
+ try:
496
+ max_row = self.fetch_one(
497
+ f"SELECT COALESCE(MAX({pk_column}), 0) AS max_id FROM {table}"
498
+ )
499
+ start = int(max_row["max_id"]) + 1 if max_row else 1
500
+ self.execute(f"CREATE SEQUENCE {seq_name} START WITH {start}")
501
+ self.commit()
502
+ row = self.fetch_one(f"SELECT nextval('{seq_name}') AS next_id")
503
+ return int(row["next_id"]) if row else start
504
+ except Exception:
505
+ pass # Fall through to sequence table
506
+
507
+ # SQLite / MySQL / MSSQL / PostgreSQL fallback — atomic sequence table
508
+ seq_key = generator_name or f"{table}.{pk_column}"
509
+ return self._sequence_next(seq_key, table=table, pk_column=pk_column)
434
510
 
435
511
  def register_function(self, name: str, num_params: int, func: callable, deterministic: bool = True):
436
512
  """Register a custom SQL function (SQLite only).