Altinity / Altinity/altinity-sql-browser
Render ClickHouse FORMAT PNG results in Workbench and Dashboard
Personne n'a encore pris cette issue.
- Langage dominant
- TypeScript
- Étoiles
- 8
- Forks
- 2
- Merge moyen
- 1 h 34 min
- PR mergées (30 j)
- 6
Description
Summary
Add first-class support for ClickHouse 26.6 FORMAT PNG results in the Workbench result area and Dashboard tiles.
ClickHouse produces the final PNG. SQL Browser transports, validates, retains, displays, and exports the returned bytes; it must not reconstruct pixels from rows.
This requires a binary result path. The current raw-format path decodes responses with Response.text(), which corrupts PNG bytes, and Dashboard execution rejects every authored FORMAT clause.
ClickHouse contract
PNG is output-only and returns raw PNG bytes by default.
- Output dimensions come from
output_format_image_widthandoutput_format_image_height, both defaulting to 1024. - Supported result shapes are RGB (
r,g,b), RGBA (r,g,b,a), or grayscale (v), optionally with integerx,ycoordinates. output_format_image_terminal_modemust remain empty for browser requests. Terminal protocols such asiterm,kitty,sixel, andautoare not browser image payloads.
Reference: ClickHouse PNG format documentation.
Format detection and scope
Treat a trailing explicit FORMAT PNG case-insensitively as an image result.
Support:
SELECT ... FORMAT PNG
SELECT ... FORMAT PNG SETTINGS output_format_image_width = 640
SELECT ... SETTINGS output_format_image_width = 640 FORMAT PNG
Use the existing SQL-aware format detector. Do not detect FORMAT PNG inside comments, strings, nested subqueries, or identifiers.
This issue supports only PNG. Other binary image formats and terminal image protocols are out of scope.
Binary transport contract
Extend the query transport so raw results can be text or bytes without converting binary data to a JavaScript string.
Recommended shape:
interface BinaryQueryBody {
bytes: Uint8Array;
contentType: string;
}
interface RunQueryResult {
error?: string;
raw?: string;
binary?: BinaryQueryBody;
streamed?: boolean;
}
Requirements:
runQuery(..., { format: 'PNG' })reads the response asArrayBufferor equivalent binary chunks.- It returns
Uint8Arraybytes andimage/pngas the normalized content type. - Text raw formats retain their current
rawstring behavior. - Streaming Table/KPI/Filter behavior remains unchanged.
- HTTP and ClickHouse errors must be parsed as text when the response is not a valid PNG payload.
- Cancellation must abort the request and must not publish a partial image.
- Never use base64 as the internal representation.
Result model
Add an explicit image result branch rather than storing PNG data in rawText.
interface ImageResultPayload {
kind: 'image';
format: 'PNG';
mimeType: 'image/png';
bytes: Uint8Array;
}
The Workbench and Dashboard may wrap this payload in their existing runtime result structures, but there must be one shared binary/image contract.
Do not serialize image bytes into saved queries, Dashboard documents, workspace persistence, history, recent values, or portable bundles. Only SQL and presentation configuration are persisted; every image is regenerated by executing the query.
Validation and resource limits
Before rendering, verify:
- payload length is non-zero;
- the first eight bytes match the PNG signature;
- the IHDR chunk is present and readable;
- width and height are positive;
- width, height, pixel count, and payload bytes stay within explicit client limits.
Initial client limits:
MAX_PNG_WIDTH = 8192;
MAX_PNG_HEIGHT = 8192;
MAX_PNG_PIXELS = 32_000_000;
MAX_PNG_BYTES = 64 * 1024 * 1024;
Reject the payload before creating an object URL when any limit fails. Show a normal result error and release the byte buffer when practical.
The existing Dashboard max_result_bytes guard may remain, but PNG validation must not depend exclusively on a ClickHouse query setting because authored SQL can override settings.
Workbench execution and rendering
For an explicit FORMAT PNG query:
- execute through the binary path;
- show one
Imageresult view instead of the normal Table / JSON / Panel switcher; - do not show PNG bytes as text;
- display the image centered in the result body;
- preserve intrinsic aspect ratio;
- use
max-width: 100%andmax-height: 100%without upscaling beyond intrinsic size by default; - allow scrolling when the intrinsic image is larger than the available result area;
- show loading, error, and cancelled states through the normal result lifecycle;
- keep Copy disabled for the binary payload;
- provide
Download PNGusing the exact returned bytes.
The image element must have an accessible name derived from the saved-query name or active tab name, with fallback PNG query result.
Object URL lifecycle
Render PNG bytes through a Blob/object URL.
- Create the URL only after payload validation succeeds.
- Revoke the previous URL before replacing it with a rerun result.
- Revoke on tab close, result replacement, view destruction, Dashboard tile destruction, route teardown, and application teardown.
- A cancelled, failed, stale, or superseded request must not leak a URL or replace a newer image.
- Detached views, when supported for this result, own and revoke their own URL.
Dashboard execution
Permit FORMAT PNG only for a panel-role tile whose resolved presentation is the image presentation defined by this issue.
Do not generally remove Dashboard's explicit-format rejection. Other authored formats remain rejected because ordinary panels require structured results.
Recommended execution ownership:
- add an image panel type, for example
panel.cfg.type = 'image'; - the image panel requires authored
FORMAT PNGin v1; panelExecution()recognizes the image panel and returnsformat: 'PNG',rowLimit: 0, and the existing read-only / byte-limit parameters;- reject an image panel whose SQL has no
FORMAT PNG; - reject
FORMAT PNGfor KPI, chart, table, logs, text, filter, or setup roles; - reject any non-PNG explicit format for the image panel.
This keeps result-format ownership explicit and prevents a saved query from silently changing an ordinary Dashboard tile into a binary result.
Image panel schema
Add image to the panel configuration union and result-choice UI.
Minimal v1 configuration:
interface ImagePanelCfgV1 {
type: 'image';
fit?: 'contain' | 'cover' | 'actual'; // default contain
background?: 'transparent' | 'checkerboard' | 'theme'; // default theme
alt?: string;
}
Rules:
containshows the complete image inside the available body.coverfills the body and may crop visually; it never changes the bytes.actualrenders at intrinsic CSS pixel size and scrolls when necessary.alt, when present, is the image accessible name. Otherwise use tile/query title.background: checkerboardhelps inspect transparent RGBA output.- Presentation variants and tile-local overrides may change fit/background/alt but may not change renderer type under existing #280 rules.
Regenerate schema-derived types, validators, schema bundles, and documentation.
Dashboard tile rendering
An image tile uses the complete tile body:
- no nested table, chart, or generic raw-text renderer;
- preserve image aspect ratio;
- center according to the chosen fit mode;
- respond to tile resizing without re-executing the query;
- use the latest successful image until a refresh succeeds, while showing a non-blocking refreshing state consistent with other tiles;
- on error, show the normal tile error and release any image produced only by the failed generation;
- stale-wave protection must prevent an older PNG response from replacing a newer result.
The tile header/footer may remain, but image content receives all remaining body space. grafana-grid@1, flow@1, mobile rendering, fallback rendering, print order, and semantic tile order remain unchanged.
Export behavior
Add PNG metadata to the existing format-to-file mapping:
PNG -> { ext: 'png', mime: 'image/png' }
Download PNG and direct export must stream or write the exact ClickHouse response bytes. Do not decode and re-encode through canvas.
Multi-statement script execution remains unchanged. Script export may export a PNG-producing statement through the existing file export path, but normal multiquery Run does not need inline image rows in the script summary for this issue.
Compatibility
- Servers that do not support
FORMAT PNGreturn their normal ClickHouse error. - No client-side version gate is required.
- Existing text raw formats retain identical behavior.
- Existing structured Workbench and Dashboard results retain identical behavior.
- PNG terminal mode is not enabled or inferred.
- External Dashboard trust preflight, read-only gates, cancellation, bounded concurrency, and no-execution-on-invalid-document rules remain in force.
Demo library
Add a small importable demo bundle or documented demo set containing:
- the 1024×1024 spiral example from this issue;
- a small RGB gradient using implicit coordinates;
- a transparent RGBA example demonstrating checkerboard background;
- one parameterized, low-cost query adapted from
ClickHouse/RayTracer.
Do not copy the entire RayTracer repository. Include only queries whose license permits redistribution, preserve required attribution, and use conservative defaults so opening the demo does not start an expensive render automatically.
Ray-tracer queries must expose small default image dimensions and sample count, and should include a warning in their saved-query description about CPU cost.
Tests
Cover:
- SQL-aware case-insensitive PNG detection with both FORMAT/SETTINGS orders;
- binary response transport without UTF-8 decoding;
- text raw formats unchanged;
- error response parsing;
- cancellation and stale-result suppression;
- PNG signature and IHDR parsing;
- byte, dimension, and pixel limits;
- Workbench Image view and download bytes;
- object URL creation and revocation on rerun, replacement, close, and teardown;
- image panel schema/type generation and result-choice selection;
- Dashboard permits image+PNG and rejects every incompatible panel/format combination;
- Dashboard image fit modes and accessible name;
- tile resize does not rerun SQL;
- exact
.pngexport metadata; - older ClickHouse rejection displays as a normal error;
- demo bundle validates and imports.
Include a real-browser test proving that a PNG response renders as a visible image in Workbench and fills the available Dashboard tile body without distortion.
Acceptance criteria
- Workbench renders valid
FORMAT PNGoutput as an image, never as decoded text. - PNG responses use a binary transport and shared image-result contract.
- Invalid or oversized PNG payloads fail before object URL creation.
- Object URLs are revoked across every replacement and teardown path.
- Exact returned bytes can be downloaded as
.pngwith MIMEimage/png. - An explicit
imagepanel type is available and schema-validated. - Dashboard accepts only the image-panel +
FORMAT PNGcombination. - Dashboard image content uses the full available tile body and preserves aspect ratio.
- Existing structured, raw-text, export, cancellation, and persistence behavior does not regress.
- Demo queries include attribution and safe default resource usage.
- Unit, integration, browser, schema-generation, and build checks pass.
Non-goals
- parsing PNG pixels into rows;
- client-side PNG generation or re-encoding;
- editing images;
- animated PNG or video playback controls;
- SVG, JPEG, WebP, PDF, or arbitrary binary preview;
- iTerm2, Kitty, Sixel, or terminal auto-detection;
- storing generated image bytes in saved queries, workspaces, history, or portable bundles;
- automatically running expensive RayTracer queries on import;
- changing Dashboard layout or multi-Dashboard architecture.
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par runQuery et le détecteur de format existant prenant en charge SQL afin de suivre le traitement des réponses brutes, puis suivez panelExecution() jusqu’au rendu des résultats dans Workbench et Dashboard. Utilisez les tests indiqués pour le transport, la validation PNG, les object-URL, le schéma, Dashboard, le navigateur et l’export comme carte de vérification ; le travail est terminé lorsque les résultats FORMAT PNG valides sont rendus et téléchargés sous forme d’octets exacts, sans régression des résultats textuels ou structurés.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- sql, typescript
- Domaine
- backend, databases, frontend
- Type d'issue
- Fonctionnalité
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- Calme
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 30/100