tina4-python 3.13.11__tar.gz → 3.13.12__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.
- {tina4_python-3.13.11 → tina4_python-3.13.12}/PKG-INFO +1 -1
- {tina4_python-3.13.11 → tina4_python-3.13.12}/pyproject.toml +1 -1
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/__init__.py +1 -1
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/adapter.py +26 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/connection.py +6 -2
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/firebird.py +13 -4
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/mssql.py +15 -7
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/mysql.py +12 -3
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/postgres.py +14 -2
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/sqlite.py +9 -2
- {tina4_python-3.13.11 → tina4_python-3.13.12}/.gitignore +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/README.md +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/ai/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/core/server.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/env.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/js/tina4-dev-admin.js +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/wsdl/__init__.py +0 -0
|
@@ -349,6 +349,32 @@ class DatabaseAdapter:
|
|
|
349
349
|
last_id=last_id,
|
|
350
350
|
)
|
|
351
351
|
|
|
352
|
+
@staticmethod
|
|
353
|
+
def _strip_trailing_semicolons(sql: str) -> str:
|
|
354
|
+
"""v3.13.12: normalize user-supplied SQL by stripping trailing
|
|
355
|
+
semicolons and whitespace.
|
|
356
|
+
|
|
357
|
+
``fetch()`` and ``fetch_one()`` wrap the user's SQL — fetch()
|
|
358
|
+
builds a ``SELECT COUNT(*) FROM ({sql}) AS _count_subquery``
|
|
359
|
+
for the pagination probe and appends ``LIMIT/OFFSET`` to the
|
|
360
|
+
real query. A trailing ``;`` in the user's SQL breaks both:
|
|
361
|
+
|
|
362
|
+
user input: "SELECT * FROM users;"
|
|
363
|
+
wrapped: "SELECT COUNT(*) FROM (SELECT * FROM users;) ..." ← syntax error
|
|
364
|
+
paginated: "SELECT * FROM users; LIMIT 100 OFFSET 0" ← invalid
|
|
365
|
+
|
|
366
|
+
Stripping trailing semicolons before any wrapping is the
|
|
367
|
+
single-line defence. Internal semicolons (between meaningful
|
|
368
|
+
SQL statements) are left alone — the driver will reject those
|
|
369
|
+
if multi-statement isn't supported on the engine.
|
|
370
|
+
"""
|
|
371
|
+
if not sql:
|
|
372
|
+
return sql
|
|
373
|
+
stripped = sql.rstrip()
|
|
374
|
+
while stripped.endswith(";"):
|
|
375
|
+
stripped = stripped[:-1].rstrip()
|
|
376
|
+
return stripped
|
|
377
|
+
|
|
352
378
|
def fetch(self, sql: str, params: list = None,
|
|
353
379
|
limit: int = 100, offset: int = 0) -> DatabaseResult:
|
|
354
380
|
"""Execute a read query and return multiple rows."""
|
|
@@ -457,8 +457,8 @@ class Database:
|
|
|
457
457
|
raise
|
|
458
458
|
|
|
459
459
|
def fetch_all(self, sql: str, params: list = None,
|
|
460
|
-
limit: int =
|
|
461
|
-
"""Fetch rows and return the records list directly.
|
|
460
|
+
limit: int = 0, offset: int = 0) -> list[dict]:
|
|
461
|
+
"""Fetch ALL rows and return the records list directly.
|
|
462
462
|
|
|
463
463
|
Symmetric with ``fetch_one``. For the common case where you just
|
|
464
464
|
want the rows and don't need the ``DatabaseResult`` metadata
|
|
@@ -469,6 +469,10 @@ class Database:
|
|
|
469
469
|
for row in rows:
|
|
470
470
|
print(row["name"])
|
|
471
471
|
|
|
472
|
+
v3.13.12: default ``limit`` is **0** (no truncation) — the method
|
|
473
|
+
name says ``fetch_all``, so it returns all matching rows. Pre-v3.13.12
|
|
474
|
+
silently truncated to 100. Pass an explicit ``limit=N`` to cap.
|
|
475
|
+
|
|
472
476
|
Returns ``[]`` (not ``None``) when no rows match.
|
|
473
477
|
"""
|
|
474
478
|
return self.fetch(sql, params, limit, offset).records
|
|
@@ -283,6 +283,9 @@ class FirebirdAdapter(DatabaseAdapter):
|
|
|
283
283
|
|
|
284
284
|
def fetch(self, sql: str, params: list = None,
|
|
285
285
|
limit: int = 100, offset: int = 0) -> DatabaseResult:
|
|
286
|
+
# v3.13.12: strip trailing `;` — Firebird wraps with COUNT(*)
|
|
287
|
+
# and appends ROWS pagination; a trailing semicolon breaks both.
|
|
288
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
286
289
|
sql = self._translate_sql(sql)
|
|
287
290
|
cursor = self._conn.cursor()
|
|
288
291
|
|
|
@@ -297,10 +300,15 @@ class FirebirdAdapter(DatabaseAdapter):
|
|
|
297
300
|
# paginated query below regardless of whether count succeeded.
|
|
298
301
|
cursor = self._conn.cursor()
|
|
299
302
|
|
|
300
|
-
# Apply Firebird pagination — ROWS start TO end
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
303
|
+
# Apply Firebird pagination — ROWS start TO end.
|
|
304
|
+
# v3.13.12: limit <= 0 means "no pagination" (fetch_all's
|
|
305
|
+
# default — give me ALL rows).
|
|
306
|
+
if limit is None or limit <= 0:
|
|
307
|
+
paginated_sql = sql
|
|
308
|
+
else:
|
|
309
|
+
start = offset + 1
|
|
310
|
+
end = offset + limit
|
|
311
|
+
paginated_sql = f"{sql} ROWS {start} TO {end}"
|
|
304
312
|
cursor = self._safe_cursor_execute(cursor, paginated_sql, params)
|
|
305
313
|
|
|
306
314
|
desc = cursor.description
|
|
@@ -310,6 +318,7 @@ class FirebirdAdapter(DatabaseAdapter):
|
|
|
310
318
|
return DatabaseResult(records=rows, count=total, limit=limit, offset=offset, sql=sql, adapter=self)
|
|
311
319
|
|
|
312
320
|
def fetch_one(self, sql: str, params: list = None) -> dict | None:
|
|
321
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
313
322
|
sql = self._translate_sql(sql)
|
|
314
323
|
cursor = self._conn.cursor()
|
|
315
324
|
cursor = self._safe_cursor_execute(cursor, sql, params)
|
|
@@ -105,6 +105,8 @@ class MSSQLAdapter(DatabaseAdapter):
|
|
|
105
105
|
|
|
106
106
|
def fetch(self, sql: str, params: list = None,
|
|
107
107
|
limit: int = 100, offset: int = 0) -> DatabaseResult:
|
|
108
|
+
# v3.13.12: strip trailing `;` — see DatabaseAdapter helper.
|
|
109
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
108
110
|
sql = self._translate_sql(sql)
|
|
109
111
|
cursor = self._conn.cursor(as_dict=True)
|
|
110
112
|
|
|
@@ -116,20 +118,26 @@ class MSSQLAdapter(DatabaseAdapter):
|
|
|
116
118
|
except Exception:
|
|
117
119
|
total = 0
|
|
118
120
|
|
|
119
|
-
# Apply pagination — MSSQL uses OFFSET/FETCH
|
|
120
|
-
#
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
# Apply pagination — MSSQL uses OFFSET/FETCH.
|
|
122
|
+
# v3.13.12: limit <= 0 means "no pagination" (fetch_all's
|
|
123
|
+
# default — give me ALL rows).
|
|
124
|
+
if limit is None or limit <= 0:
|
|
125
|
+
paginated_sql = sql
|
|
126
|
+
paginated_params = tuple(params or [])
|
|
123
127
|
else:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
# OFFSET/FETCH requires an ORDER BY; if none exists, add a default
|
|
129
|
+
if not re.search(r"\bORDER\s+BY\b", sql, re.IGNORECASE):
|
|
130
|
+
paginated_sql = f"{sql} ORDER BY (SELECT NULL) OFFSET %s ROWS FETCH NEXT %s ROWS ONLY"
|
|
131
|
+
else:
|
|
132
|
+
paginated_sql = f"{sql} OFFSET %s ROWS FETCH NEXT %s ROWS ONLY"
|
|
133
|
+
paginated_params = tuple(params or []) + (offset, limit)
|
|
127
134
|
cursor.execute(paginated_sql, paginated_params)
|
|
128
135
|
rows = [dict(row) for row in cursor.fetchall()]
|
|
129
136
|
|
|
130
137
|
return DatabaseResult(records=rows, count=total, limit=limit, offset=offset, sql=sql, adapter=self)
|
|
131
138
|
|
|
132
139
|
def fetch_one(self, sql: str, params: list = None) -> dict | None:
|
|
140
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
133
141
|
sql = self._translate_sql(sql)
|
|
134
142
|
cursor = self._conn.cursor(as_dict=True)
|
|
135
143
|
cursor.execute(sql, tuple(params) if params else ())
|
|
@@ -94,6 +94,9 @@ class MySQLAdapter(DatabaseAdapter):
|
|
|
94
94
|
|
|
95
95
|
def fetch(self, sql: str, params: list = None,
|
|
96
96
|
limit: int = 100, offset: int = 0) -> DatabaseResult:
|
|
97
|
+
# v3.13.12: strip trailing `;` before wrapping with COUNT(*)
|
|
98
|
+
# and appending LIMIT/OFFSET — see DatabaseAdapter helper.
|
|
99
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
97
100
|
sql = self._translate_sql(sql)
|
|
98
101
|
cursor = self._conn.cursor(dictionary=True)
|
|
99
102
|
|
|
@@ -105,15 +108,21 @@ class MySQLAdapter(DatabaseAdapter):
|
|
|
105
108
|
except Exception:
|
|
106
109
|
total = 0
|
|
107
110
|
|
|
108
|
-
# Apply pagination
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
# Apply pagination — v3.13.12: limit <= 0 means "no pagination"
|
|
112
|
+
# (fetch_all's default — give me ALL rows).
|
|
113
|
+
if limit is None or limit <= 0:
|
|
114
|
+
paginated_sql = sql
|
|
115
|
+
paginated_params = params or []
|
|
116
|
+
else:
|
|
117
|
+
paginated_sql = f"{sql} LIMIT %s OFFSET %s"
|
|
118
|
+
paginated_params = (params or []) + [limit, offset]
|
|
111
119
|
cursor.execute(paginated_sql, paginated_params)
|
|
112
120
|
rows = [dict(row) for row in cursor.fetchall()]
|
|
113
121
|
|
|
114
122
|
return DatabaseResult(records=rows, count=total, limit=limit, offset=offset, sql=sql, adapter=self)
|
|
115
123
|
|
|
116
124
|
def fetch_one(self, sql: str, params: list = None) -> dict | None:
|
|
125
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
117
126
|
sql = self._translate_sql(sql)
|
|
118
127
|
cursor = self._conn.cursor(dictionary=True)
|
|
119
128
|
cursor.execute(sql, params or [])
|
|
@@ -306,6 +306,10 @@ class PostgreSQLAdapter(DatabaseAdapter):
|
|
|
306
306
|
limit: int = 100, offset: int = 0) -> DatabaseResult:
|
|
307
307
|
import psycopg2.extras
|
|
308
308
|
|
|
309
|
+
# v3.13.12: strip trailing `;` before the framework wraps with
|
|
310
|
+
# COUNT(*) and appends LIMIT/OFFSET — otherwise a user-supplied
|
|
311
|
+
# `"SELECT * FROM users;"` produces invalid wrapped SQL.
|
|
312
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
309
313
|
sql = self._translate_sql(sql)
|
|
310
314
|
|
|
311
315
|
# v3.13.8: heal first so the COUNT probe below doesn't open on a
|
|
@@ -342,8 +346,14 @@ class PostgreSQLAdapter(DatabaseAdapter):
|
|
|
342
346
|
# Apply pagination — the real query, so use the error-handling
|
|
343
347
|
# wrapper. The count probe above is best-effort and stays on
|
|
344
348
|
# _safe_execute (a failed probe shouldn't taint last_error).
|
|
345
|
-
|
|
346
|
-
|
|
349
|
+
# v3.13.12: limit <= 0 means "no pagination" (fetch_all's
|
|
350
|
+
# default — give me ALL rows, not a silent first-100 slice).
|
|
351
|
+
if limit is None or limit <= 0:
|
|
352
|
+
paginated_sql = sql
|
|
353
|
+
paginated_params = params or []
|
|
354
|
+
else:
|
|
355
|
+
paginated_sql = f"{sql} LIMIT %s OFFSET %s"
|
|
356
|
+
paginated_params = (params or []) + [limit, offset]
|
|
347
357
|
self._exec_with_handling(cursor, paginated_sql, paginated_params)
|
|
348
358
|
rows = [self._decode_blobs(dict(row)) for row in cursor.fetchall()]
|
|
349
359
|
|
|
@@ -352,6 +362,8 @@ class PostgreSQLAdapter(DatabaseAdapter):
|
|
|
352
362
|
def fetch_one(self, sql: str, params: list = None) -> dict | None:
|
|
353
363
|
import psycopg2.extras
|
|
354
364
|
|
|
365
|
+
# v3.13.12: see fetch() — trailing semicolons break the wrappers.
|
|
366
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
355
367
|
sql = self._translate_sql(sql)
|
|
356
368
|
cursor = self._conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
|
357
369
|
self._exec_with_handling(cursor, sql, params)
|
|
@@ -108,6 +108,8 @@ class SQLiteAdapter(DatabaseAdapter):
|
|
|
108
108
|
|
|
109
109
|
def fetch(self, sql: str, params: list = None,
|
|
110
110
|
limit: int = 100, offset: int = 0) -> DatabaseResult:
|
|
111
|
+
# v3.13.12: strip trailing `;` before wrapping — see DatabaseAdapter.
|
|
112
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
111
113
|
# Count total rows (without LIMIT/OFFSET)
|
|
112
114
|
count_sql = f"SELECT COUNT(*) as cnt FROM ({sql})"
|
|
113
115
|
try:
|
|
@@ -115,8 +117,12 @@ class SQLiteAdapter(DatabaseAdapter):
|
|
|
115
117
|
except Exception:
|
|
116
118
|
total = 0
|
|
117
119
|
|
|
118
|
-
# Apply pagination — skip if SQL already has LIMIT
|
|
119
|
-
|
|
120
|
+
# Apply pagination — skip if SQL already has LIMIT, or if
|
|
121
|
+
# limit <= 0 (v3.13.12: fetch_all's "give me all rows" path).
|
|
122
|
+
if limit is None or limit <= 0:
|
|
123
|
+
paginated_sql = sql
|
|
124
|
+
paginated_params = params or []
|
|
125
|
+
elif "LIMIT" in sql.upper().split("--")[0]:
|
|
120
126
|
paginated_sql = sql
|
|
121
127
|
paginated_params = params or []
|
|
122
128
|
else:
|
|
@@ -128,6 +134,7 @@ class SQLiteAdapter(DatabaseAdapter):
|
|
|
128
134
|
return DatabaseResult(records=rows, count=total, limit=limit, offset=offset, sql=sql, adapter=self)
|
|
129
135
|
|
|
130
136
|
def fetch_one(self, sql: str, params: list = None) -> dict | None:
|
|
137
|
+
sql = self._strip_trailing_semicolons(sql)
|
|
131
138
|
cursor = self._conn.execute(sql, params or [])
|
|
132
139
|
row = cursor.fetchone()
|
|
133
140
|
return dict(row) if row else None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/images/tina4-logo-icon.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/queue_backends/rabbitmq_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/templates/docker/python/Dockerfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.11 → tina4_python-3.13.12}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|