tina4-python 3.13.123__tar.gz → 3.13.125__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 (170) hide show
  1. {tina4_python-3.13.123 → tina4_python-3.13.125}/PKG-INFO +1 -1
  2. {tina4_python-3.13.123 → tina4_python-3.13.125}/pyproject.toml +1 -1
  3. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/__init__.py +1 -1
  4. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/cli/__init__.py +4 -4
  5. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/firebird.py +1 -0
  6. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/mssql.py +1 -0
  7. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/mysql.py +1 -0
  8. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/sql_translator.py +43 -0
  9. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/session/__init__.py +21 -7
  10. {tina4_python-3.13.123 → tina4_python-3.13.125}/.gitignore +0 -0
  11. {tina4_python-3.13.123 → tina4_python-3.13.125}/README.md +0 -0
  12. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/CLAUDE.md +0 -0
  13. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/HtmlElement.py +0 -0
  14. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/Testing.py +0 -0
  15. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/_import_helper.py +0 -0
  16. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/ai/__init__.py +0 -0
  17. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/ai/client.py +0 -0
  18. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/api/__init__.py +0 -0
  19. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/auth/__init__.py +0 -0
  20. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/cache/__init__.py +0 -0
  21. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/container/__init__.py +0 -0
  22. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/context/__init__.py +0 -0
  23. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/context/chunker.py +0 -0
  24. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/__init__.py +0 -0
  25. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/cache.py +0 -0
  26. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/constants.py +0 -0
  27. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/events.py +0 -0
  28. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/middleware.py +0 -0
  29. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/port_takeover.py +0 -0
  30. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/rate_limiter.py +0 -0
  31. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/request.py +0 -0
  32. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/response.py +0 -0
  33. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/router.py +0 -0
  34. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/core/server.py +0 -0
  35. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/crud/__init__.py +0 -0
  36. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/__init__.py +0 -0
  37. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/adapter.py +0 -0
  38. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/connection.py +0 -0
  39. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/database_url.py +0 -0
  40. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/mongodb.py +0 -0
  41. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/odbc.py +0 -0
  42. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/postgres.py +0 -0
  43. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/database/sqlite.py +0 -0
  44. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/debug/__init__.py +0 -0
  45. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/debug/error_overlay.py +0 -0
  46. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/dev_admin/__init__.py +0 -0
  47. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/dev_admin/metrics.py +0 -0
  48. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/dev_admin/plan.py +0 -0
  49. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/dev_admin/project_index.py +0 -0
  50. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/docs.py +0 -0
  51. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/docstore/__init__.py +0 -0
  52. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/dotenv/__init__.py +0 -0
  53. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/env.py +0 -0
  54. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/frond/FROND.md +0 -0
  55. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/frond/__init__.py +0 -0
  56. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/frond/compiler.py +0 -0
  57. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/frond/engine.py +0 -0
  58. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/frond/parser.py +0 -0
  59. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/auth/meta.json +0 -0
  60. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  61. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/database/meta.json +0 -0
  62. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  63. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/error-overlay/meta.json +0 -0
  64. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  65. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/orm/meta.json +0 -0
  66. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  67. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  68. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/queue/meta.json +0 -0
  69. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  70. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/rest-api/meta.json +0 -0
  71. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  72. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/templates/meta.json +0 -0
  73. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  74. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  75. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graph/__init__.py +0 -0
  76. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graph/adapter.py +0 -0
  77. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graph/adapters/__init__.py +0 -0
  78. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graph/adapters/arango.py +0 -0
  79. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graph/adapters/bolt.py +0 -0
  80. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graph/adapters/ultipa.py +0 -0
  81. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graph/graph_url.py +0 -0
  82. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/graphql/__init__.py +0 -0
  83. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/i18n/__init__.py +0 -0
  84. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/mcp/__init__.py +0 -0
  85. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/mcp/protocol.py +0 -0
  86. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/mcp/tools.py +0 -0
  87. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/messenger/__init__.py +0 -0
  88. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/migration/__init__.py +0 -0
  89. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/migration/runner.py +0 -0
  90. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/mqtt/__init__.py +0 -0
  91. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/mqtt/message.py +0 -0
  92. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/orm/__init__.py +0 -0
  93. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/orm/fields.py +0 -0
  94. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/orm/model.py +0 -0
  95. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/orm/point.py +0 -0
  96. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/__feedback/widget.js +0 -0
  97. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/css/tina4.css +0 -0
  98. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/css/tina4.min.css +0 -0
  99. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/favicon.ico +0 -0
  100. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/images/logo.svg +0 -0
  101. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  102. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/js/frond.js +0 -0
  103. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/js/frond.min.js +0 -0
  104. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  105. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/js/tina4.min.js +0 -0
  106. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/js/tina4js.min.js +0 -0
  107. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/swagger/index.html +0 -0
  108. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  109. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/query_builder/__init__.py +0 -0
  110. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue/__init__.py +0 -0
  111. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue/amqp_url.py +0 -0
  112. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue/job.py +0 -0
  113. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue/kafka_backend.py +0 -0
  114. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue/lite_backend.py +0 -0
  115. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue/mongo_backend.py +0 -0
  116. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue/rabbitmq_backend.py +0 -0
  117. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue_backends/__init__.py +0 -0
  118. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue_backends/kafka_backend.py +0 -0
  119. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue_backends/mongo_backend.py +0 -0
  120. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  121. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/__init__.py +0 -0
  122. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/models/Attachment.py +0 -0
  123. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/models/Channel.py +0 -0
  124. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/models/ChannelMember.py +0 -0
  125. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/models/Message.py +0 -0
  126. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/models/Workspace.py +0 -0
  127. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/models/__init__.py +0 -0
  128. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/realtime/storage.py +0 -0
  129. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/seeder/__init__.py +0 -0
  130. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/service/__init__.py +0 -0
  131. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/session_handlers/__init__.py +0 -0
  132. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/session_handlers/memcached_handler.py +0 -0
  133. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  134. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/session_handlers/redis_handler.py +0 -0
  135. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/session_handlers/valkey_handler.py +0 -0
  136. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/sso.py +0 -0
  137. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/swagger/__init__.py +0 -0
  138. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/components/crud.twig +0 -0
  139. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  140. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  141. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/docker/python/Dockerfile +0 -0
  142. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  143. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/302.twig +0 -0
  144. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/401.twig +0 -0
  145. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/403.twig +0 -0
  146. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/404.twig +0 -0
  147. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/500.twig +0 -0
  148. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/502.twig +0 -0
  149. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/503.twig +0 -0
  150. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/errors/base.twig +0 -0
  151. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/frontend/README.md +0 -0
  152. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/templates/readme.md +0 -0
  153. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/test/__init__.py +0 -0
  154. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/test_client/__init__.py +0 -0
  155. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  156. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  157. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  158. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  159. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  160. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  161. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  162. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  163. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  164. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  165. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  166. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  167. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/validator/__init__.py +0 -0
  168. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/websocket/__init__.py +0 -0
  169. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/websocket/backplane.py +0 -0
  170. {tina4_python-3.13.123 → tina4_python-3.13.125}/tina4_python/wsdl/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tina4-python
3
- Version: 3.13.123
3
+ Version: 3.13.125
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.123"
3
+ version = "3.13.125"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -51,7 +51,7 @@ def _resolve_version() -> str:
51
51
  #
52
52
  # test_version_constant.py now asserts this literal equals the pyproject
53
53
  # version, so the release bump cannot leave it behind again.
54
- return "3.13.123"
54
+ return "3.13.125"
55
55
 
56
56
 
57
57
  __version__ = _resolve_version()
@@ -31,8 +31,8 @@ from pathlib import Path
31
31
 
32
32
  # ── Field type mapping ────────────────────────────────────────────────
33
33
  FIELD_TYPE_MAP = {
34
- "string": {"orm": "StringField", "sql": "TEXT", "default": "''"},
35
- "str": {"orm": "StringField", "sql": "TEXT", "default": "''"},
34
+ "string": {"orm": "StringField", "sql": "VARCHAR(255)", "default": "''"},
35
+ "str": {"orm": "StringField", "sql": "VARCHAR(255)", "default": "''"},
36
36
  "int": {"orm": "IntegerField", "sql": "INTEGER", "default": "0"},
37
37
  "integer": {"orm": "IntegerField", "sql": "INTEGER", "default": "0"},
38
38
  "float": {"orm": "NumericField", "sql": "REAL", "default": "0"},
@@ -41,7 +41,7 @@ FIELD_TYPE_MAP = {
41
41
  "bool": {"orm": "BooleanField", "sql": "INTEGER", "default": "0"},
42
42
  "boolean": {"orm": "BooleanField", "sql": "INTEGER", "default": "0"},
43
43
  "text": {"orm": "TextField", "sql": "TEXT", "default": "''"},
44
- "datetime": {"orm": "DateTimeField", "sql": "TEXT", "default": "NULL"},
44
+ "datetime": {"orm": "DateTimeField", "sql": "TIMESTAMP", "default": "NULL"},
45
45
  "blob": {"orm": "BlobField", "sql": "BLOB", "default": "NULL"},
46
46
  }
47
47
 
@@ -1941,7 +1941,7 @@ def _gen_migration(name: str, flags: dict = None, *,
1941
1941
  info = FIELD_TYPE_MAP.get(ftype, FIELD_TYPE_MAP["string"])
1942
1942
  default = f" DEFAULT {info['default']}" if info["default"] != "NULL" else ""
1943
1943
  col_lines.append(f" {fname} {info['sql']}{default}")
1944
- col_lines.append(" created_at TEXT DEFAULT CURRENT_TIMESTAMP")
1944
+ col_lines.append(" created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
1945
1945
 
1946
1946
  # ADR-0063: `tina4:edit` marker between the last column and the closing
1947
1947
  # paren, placed OUTSIDE the comma-joined column list so the SQL stays
@@ -646,6 +646,7 @@ class FirebirdAdapter(SqlCrudMixin, DatabaseAdapter):
646
646
  sql = SQLTranslator.ilike_to_like(sql)
647
647
  sql = SQLTranslator.boolean_to_int(sql)
648
648
  sql = SQLTranslator.auto_increment_syntax(sql, "firebird")
649
+ sql = SQLTranslator.ddl_types(sql, "firebird")
649
650
  return sql
650
651
 
651
652
  def _supports_returning(self) -> bool:
@@ -350,6 +350,7 @@ class MSSQLAdapter(SqlCrudMixin, DatabaseAdapter):
350
350
  sql = SQLTranslator.ilike_to_like(sql)
351
351
  sql = SQLTranslator.auto_increment_syntax(sql, "mssql")
352
352
  sql = SQLTranslator.boolean_to_int(sql)
353
+ sql = SQLTranslator.ddl_types(sql, "mssql")
353
354
  return sql
354
355
 
355
356
  def _supports_returning(self) -> bool:
@@ -321,6 +321,7 @@ class MySQLAdapter(SqlCrudMixin, DatabaseAdapter):
321
321
  sql = SQLTranslator.concat_pipes_to_func(sql)
322
322
  sql = SQLTranslator.ilike_to_like(sql)
323
323
  sql = SQLTranslator.auto_increment_syntax(sql, "mysql")
324
+ sql = SQLTranslator.ddl_types(sql, "mysql")
324
325
  return sql
325
326
 
326
327
  def _supports_returning(self) -> bool:
@@ -347,6 +347,49 @@ class SQLTranslator:
347
347
  return re.sub(r"\s*AUTOINCREMENT\b", "", sql, flags=re.IGNORECASE)
348
348
  return sql
349
349
 
350
+ @staticmethod
351
+ def ddl_types(sql: str, engine: str) -> str:
352
+ """Translate SQLite-canonical DDL column TYPES + CREATE-TABLE options to
353
+ the target engine.
354
+
355
+ ONLY acts on ``CREATE TABLE`` / ``ALTER TABLE`` statements, so a query or
356
+ INSERT that happens to contain the word ``TEXT`` (a column name, a string
357
+ literal) is never rewritten. Complements ``auto_increment_syntax`` (which
358
+ maps the id keyword) so ONE portable migration — and every
359
+ ``ORM.create_table()`` DDL, which is also SQLite-canonical — applies on
360
+ every engine instead of failing on Firebird/MSSQL.
361
+
362
+ * Firebird has no ``TEXT`` (-607), no ``REAL``, and no
363
+ ``CREATE TABLE IF NOT EXISTS``.
364
+ * MSSQL has no ``CREATE TABLE IF NOT EXISTS`` and its ``TIMESTAMP`` is a
365
+ rowversion, not a datetime — a ``created_at TIMESTAMP`` there is wrong.
366
+ * MySQL's ``TIMESTAMP`` carries auto-update / 2038 surprises, so a
367
+ datetime column maps to ``DATETIME`` (matching ORM.create_table()).
368
+ """
369
+ # Gate to DDL only, tolerating leading `-- ...` comment lines / blank
370
+ # lines that a migration file carries before its CREATE TABLE. A SELECT
371
+ # or INSERT that merely mentions a type keyword is never rewritten.
372
+ head = re.sub(r"^(?:\s*--[^\n]*\n)+", "", sql)
373
+ if not re.match(r"\s*(CREATE\s+TABLE|ALTER\s+TABLE)\b", head, re.IGNORECASE):
374
+ return sql
375
+ e = (engine or "").lower()
376
+ if e == "firebird":
377
+ sql = re.sub(r"\bIF\s+NOT\s+EXISTS\b", "", sql, flags=re.IGNORECASE)
378
+ # Map bare TEXT -> BLOB SUB_TYPE TEXT, but leave an existing
379
+ # "BLOB SUB_TYPE TEXT" intact (it already contains the word TEXT).
380
+ sql = re.sub(
381
+ r"\bBLOB\s+SUB_TYPE\s+TEXT\b", "\x00FBTEXT\x00", sql, flags=re.IGNORECASE
382
+ )
383
+ sql = re.sub(r"\bTEXT\b", "BLOB SUB_TYPE TEXT", sql, flags=re.IGNORECASE)
384
+ sql = sql.replace("\x00FBTEXT\x00", "BLOB SUB_TYPE TEXT")
385
+ sql = re.sub(r"\bREAL\b", "DOUBLE PRECISION", sql, flags=re.IGNORECASE)
386
+ elif e == "mssql":
387
+ sql = re.sub(r"\bIF\s+NOT\s+EXISTS\b", "", sql, flags=re.IGNORECASE)
388
+ sql = re.sub(r"\bTIMESTAMP\b", "DATETIME2", sql, flags=re.IGNORECASE)
389
+ elif e == "mysql":
390
+ sql = re.sub(r"\bTIMESTAMP\b", "DATETIME", sql, flags=re.IGNORECASE)
391
+ return sql
392
+
350
393
  @staticmethod
351
394
  def placeholder_style(sql: str, style: str = "?") -> str:
352
395
  """Convert ? placeholders to engine-specific style.
@@ -174,18 +174,32 @@ class DatabaseSessionHandler(SessionHandler):
174
174
  # The table is now created on first use, inside that policy.
175
175
  self._table_ready = False
176
176
 
177
+ # The portable statement that shipped for every engine. VARCHAR(255) carries
178
+ # TEXT affinity and DOUBLE PRECISION an 8-byte float on SQLite; it is DOUBLE
179
+ # PRECISION (never REAL - REAL is single-precision float4 on PostgreSQL, which
180
+ # test_session_expiry_contract pins) on the networked engines. Correct on
181
+ # sqlite/postgres/mysql/mssql, which is why it was never per-engine before.
182
+ _CREATE_TABLE_DEFAULT = "CREATE TABLE tina4_session (session_id VARCHAR(255) PRIMARY KEY, data TEXT NOT NULL, expires_at DOUBLE PRECISION NOT NULL)"
183
+ # Firebird is the one engine that needs a different statement: it has NO TEXT
184
+ # type, so the payload must be a VARCHAR (ceiling 8191 characters on this
185
+ # engine alone), matching PHP/Ruby/Node. The prior single "data TEXT" CREATE
186
+ # failed on Firebird with -607 "source column TEXT does not exist", so the
187
+ # database session backend never worked there. table_exists() below covers
188
+ # Firebird's lack of IF NOT EXISTS.
189
+ _CREATE_TABLE_SQL = {
190
+ "firebird": "CREATE TABLE tina4_session (session_id VARCHAR(255) NOT NULL PRIMARY KEY, data VARCHAR(8191) NOT NULL, expires_at DOUBLE PRECISION NOT NULL)",
191
+ }
192
+
177
193
  def _ensure_table(self):
178
194
  if self._table_ready:
179
195
  return
180
196
  self._table_ready = True
181
197
  if not self._db.table_exists("tina4_session"):
182
- self._db.execute("""
183
- CREATE TABLE tina4_session (
184
- session_id VARCHAR(255) PRIMARY KEY,
185
- data TEXT NOT NULL,
186
- expires_at DOUBLE PRECISION NOT NULL
187
- )
188
- """)
198
+ try:
199
+ engine = (self._db.get_database_type() or "").lower()
200
+ except Exception:
201
+ engine = ""
202
+ self._db.execute(self._CREATE_TABLE_SQL.get(engine, self._CREATE_TABLE_DEFAULT))
189
203
  self._db.commit()
190
204
 
191
205
  def read(self, session_id: str) -> dict: