Skip to content

feat: add Russian i18n and SELECT workflow result export - #3247

Open
BSardorbek wants to merge 3 commits into
hhyo:masterfrom
BSardorbek:feat/l18n-select-result-export
Open

feat: add Russian i18n and SELECT workflow result export#3247
BSardorbek wants to merge 3 commits into
hhyo:masterfrom
BSardorbek:feat/l18n-select-result-export

Conversation

@BSardorbek

Copy link
Copy Markdown

Summary

  • Add Russian (ru) locale catalogs while keeping the default language as zh-hans.
  • Allow SELECT statements on PgSQL and ClickHouse SQL workflow tickets (SysConfig allow_select_in_workflow, default on).
  • After a ticket finishes, view SELECT rows in a new tab or download them as CSV/JSON from the ticket detail page.

Test plan

  • Submit a PgSQL or ClickHouse SELECT workflow, approve and execute it.
  • On the ticket detail page, use 查看结果 / CSV / JSON.
  • Switch UI language to Russian via /i18n/setlang/ and confirm locale files load.
  • Mixed SELECT + DML/DDL in one ticket is still rejected.

Allow SELECT on PgSQL/ClickHouse workflow tickets and download finished rows as CSV/JSON, plus Russian locale catalogs without changing the default zh-hans language.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7c4b4d3279

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread sql/engines/pgsql.py
affected_rows=len(rows),
execute_time=t.cost,
select_columns=colnames,
select_rows=serialize_select_rows(rows),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Apply configured masking before persisting SELECT rows

When data_masking is enabled and a PostgreSQL column has an active masking rule, the normal query path calls query_masking in sql/services/sqlquery_service.py:107-112, but this workflow path serializes the raw cursor rows directly. The unmasked values are then persisted in execute_result and exposed to every user allowed to view the workflow, bypassing the deployment's data-protection configuration; construct a result set and run the same masking step before saving these rows.

Useful? React with 👍 / 👎.

Comment thread sql/templates/select_result.html Outdated
Comment on lines +33 to +34
var columns = {{ columns_json|safe }};
var rows = {{ rows_json|safe }};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Embed result data with script-safe JSON

When a selected value or column alias contains a string such as </script><script>…, JSON serialization leaves the closing tag intact and safe inserts it directly into an executable script block. The browser terminates the original block before Bootstrap Table's escape option is reached, so anyone able to influence queried database data can execute JavaScript when an authorized user opens the result view; use Django's json_script or otherwise escape HTML-closing characters before embedding the JSON.

Useful? React with 👍 / 👎.

Comment thread sql/sql_workflow.py Outdated
)

if fmt == "json":
records = [dict(zip(columns, row)) for row in data_rows]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve duplicate columns in JSON exports

When a query returns duplicate column labels, as is common with SELECT a.id, b.id ..., converting each row with dict(zip(...)) silently overwrites the earlier value under the repeated key. The CSV and table views retain both values, but the JSON download corrupts the result; disambiguate duplicate labels or export the columns and row arrays separately.

Useful? React with 👍 / 👎.

Comment thread sql/permission_i18n.py Outdated
Comment on lines +25 to +28
("可以添加 ", "Добавить: "),
("可以修改 ", "Изменить: "),
("可以删除 ", "Удалить: "),
("可以查看 ", "Просмотр: "),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the active language when rewriting permission names

When the post-migrate hook runs under the configured default LANGUAGE_CODE = "zh-hans", an already-Chinese built-in permission name that lacks an exact full-string gettext match enters this fallback and is unconditionally rewritten with a Russian prefix. Consequently routine migrations can make the default Chinese admin and group permission forms display Russian or mixed-language names; choose the replacement prefix from the requested language rather than always using Russian.

Useful? React with 👍 / 👎.

Comment thread sql/engines/pgsql.py Outdated
Comment on lines +28 to +31
def _is_select_statement(statement: str) -> bool:
"""True for SELECT / WITH ... SELECT (CTE)."""
s = (statement or "").strip().lower()
return bool(re.match(r"^(with\b|select\b)", s))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Distinguish data-changing WITH statements from SELECT CTEs

A PostgreSQL statement beginning with WITH is not necessarily a query; for example, WITH source AS (...) INSERT INTO target SELECT ... is a valid data-changing workflow statement. This predicate now classifies every such statement as SELECT, so a single-statement ticket passes the SELECT audit path and is then executed in the READ ONLY transaction selected at pgsql.py:393-399, where PostgreSQL rejects the INSERT/UPDATE/DELETE. Parse or inspect the CTE's main statement before choosing the audit and transaction paths.

Useful? React with 👍 / 👎.

Comment thread sql/engines/pgsql.py Outdated
Comment on lines 408 to 409
with FuncTimer() as t:
cursor.execute(statement)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Apply the row limit before executing PostgreSQL SELECTs

When an approved SELECT omits its own LIMIT, admin_query_limit is only passed to fetchmany after cursor.execute has completed; unlike the normal query service, this path never calls filter_sql. A large or generated result can therefore be fully evaluated and buffered by the database/client despite a small configured limit, potentially exhausting a worker or database resources. Rewrite the statement with the configured limit or use a server-side cursor before executing it.

Useful? React with 👍 / 👎.

Comment thread sql/sql_workflow.py Outdated
Comment on lines +256 to +257
for row in data_rows:
writer.writerow("" if v is None else v for v in row)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Neutralize spreadsheet formulas in CSV output

When a selected string begins with =, +, -, or @, csv.writer preserves it as a formula-capable cell. If database content is attacker-controlled and a reviewer opens the downloaded file in Excel or another spreadsheet, the value can execute a formula or trigger an external-data request; prefix formula-like string cells with a safe character before writing the export.

Useful? React with 👍 / 👎.

Comment thread sql/engines/pgsql.py Outdated
Comment on lines +416 to +417
rows = cursor.fetchmany(select_limit)
truncated = len(rows) >= select_limit

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify an extra row before marking results truncated

When a query naturally returns exactly admin_query_limit rows, fetchmany(select_limit) returns that exact count and this condition marks the result as truncated even though no row was omitted. Users then receive a false truncation warning; fetch one additional row, set the flag only when that extra row exists, and discard it from the stored preview. The parallel >= select_limit check in the ClickHouse workflow has the same issue.

Useful? React with 👍 / 👎.

Comment thread sql/templates/select_result.html Outdated
Comment on lines +45 to +46
$("#tb-select-result").bootstrapTable({
locale: window.ARCHERY_LOCALE,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register the Russian Bootstrap Table locale

When the request language is Russian, window.ARCHERY_LOCALE is never defined anywhere in the repository, while common/templates/base.html:420 always loads bootstrap-table-zh-CN.min.js and makes Chinese the plugin default. Passing this undefined value therefore leaves the new result table's search, pagination, column, and export controls in Chinese; load/register a Russian Bootstrap Table locale and derive this option from the active request language.

Useful? React with 👍 / 👎.

Comment thread sql/templates/detail.html Outdated
Comment on lines +660 to +664
var html = '<a href="' + view + '" target="_blank">查看结果</a>'
+ ' · <a href="' + csv + '">CSV</a>'
+ ' · <a href="' + js + '">JSON</a>';
if (row.select_truncated) {
html += '<br><span class="text-muted">结果已截断</span>';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Translate the new result links in Russian sessions

When a user selects Russian, these newly introduced labels still render as the literal Chinese strings 查看结果 and 结果已截断 because they are not passed through gettext, even though Russian translations for both msgids were added to the JavaScript catalogs in this commit. Wrap the labels with gettext(...) or render translated template strings so the primary entry point to the new result feature follows the active locale.

Useful? React with 👍 / 👎.

CI lint failed because new files were not black-formatted. Workflow tests also hit SysConfig without a DB, and GaussDB inherited the new SELECT-in-workflow path.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.15470% with 93 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.31%. Comparing base (fa0c106) to head (65c9575).

Files with missing lines Patch % Lines
sql/engines/pgsql.py 68.53% 45 Missing ⚠️
sql/sql_workflow.py 86.51% 12 Missing ⚠️
sql/engines/models.py 57.14% 9 Missing ⚠️
sql/management/commands/sync_permission_names.py 0.00% 9 Missing ⚠️
sql/permission_i18n.py 88.05% 8 Missing ⚠️
sql/engines/test_clickhouse.py 92.04% 7 Missing ⚠️
sql/engines/clickhouse.py 95.45% 2 Missing ⚠️
sql/signals.py 95.45% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3247      +/-   ##
==========================================
- Coverage   88.35%   88.31%   -0.04%     
==========================================
  Files         157      163       +6     
  Lines       29141    29841     +700     
==========================================
+ Hits        25747    26354     +607     
- Misses       3394     3487      +93     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Apply configured masking and server-side row limits on workflow SELECTs, treat mutating WITH statements as DML, and harden CSV/JSON/HTML result export.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant