feat: script export — multi-statement follow-up to streaming Export (#99) by BorisTyshkevich · Pull Request #104 · Altinity/altinity-sql-browser · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions CHANGELOG.md
25 changes: 23 additions & 2 deletions src/core/export.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// Pure serializers + file-naming helpers for exporting result data. `toTSV`
// backs the results pane's Copy (pastes into spreadsheets); `formatFileMeta` /
// `exportFilename` back the streaming Export button (issue #87), which streams
// a ClickHouse response straight to disk rather than serializing rows itself.
// No DOM, no globals.
// a ClickHouse response straight to disk rather than serializing rows itself;
// `scriptExportName` backs the multi-statement script export (issue #99). No
// DOM, no globals.

import { inferQueryName } from './format.js';

function cell(v) {
return v == null ? '' : String(v);
Expand Down Expand Up @@ -54,3 +57,21 @@ export function exportFilename(tabName, now, ext) {
|| 'export-' + new Date(now).toISOString().replace(/[:.]/g, '-');
return base + '.' + (ext || 'tsv');
}

/**
* Deterministic per-statement export filename: `<NNN>-<slug>.<ext>` (e.g.
* `001-select.tsv`). `index` is the statement's 0-based position in the script;
* the prefix is `index+1` zero-padded to 3, so it matches the log pane's `#`
* column (non-row statements consume a number, leaving intentional gaps). `slug`
* comes from inferQueryName → sanitized, lowercased, ≤ 24 chars (empty → 'query').
* `taken` (Set of names already used this run) de-dupes with `-2`, `-3`, …
* Pure — the caller adds the returned name to `taken`.
*/
export function scriptExportName(index, stmt, ext, taken) {
const num = String(index + 1).padStart(3, '0');
const slug = (inferQueryName(stmt).replace(/^Query · /, '') || stmt)
.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 24) || 'query';
let name = `${num}-${slug}.${ext}`;
for (let n = 2; taken && taken.has(name); n++) name = `${num}-${slug}-${n}.${ext}`;
return name;
}
23 changes: 23 additions & 0 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,29 @@ table.res-table tbody tr:hover td.idx { background: var(--bg-hover); }
border-top: 1px solid var(--border);
}

/* script-export log pane (#99) */
.script-export-grid table.res-table { table-layout: fixed; width: 100%; }
.script-export-grid th.se-num, .script-export-grid td.se-num { width: 34px; text-align: center; color: var(--fg-faint); }
.script-export-grid th.se-sql { width: 28%; }
.script-export-grid th.se-type, .script-export-grid td.se-type { width: 60px; color: var(--fg-faint); }
.script-export-grid th.se-status { width: 90px; }
.script-export-grid th.se-file { width: 22%; }
.script-export-grid th.se-bytes, .script-export-grid td.se-bytes { width: 80px; text-align: right; }
.script-export-grid th.se-time, .script-export-grid td.se-time { width: 80px; text-align: right; }
.script-export-grid th { position: relative; }
.script-export-grid td.se-sql .cell-val { font-family: var(--mono); color: var(--fg-mute); }
.script-export-grid td.se-file { font-family: var(--mono); font-size: 11px; color: var(--fg-mute); overflow: hidden; text-overflow: ellipsis; }
.script-export-grid td.se-bytes, .script-export-grid td.se-time { font-family: var(--mono); font-size: 11px; color: var(--fg-faint); white-space: nowrap; }
.se-status-cell { font-family: var(--mono); font-size: 12px; text-transform: capitalize; }
.se-status-cell.ok { color: #10b981; font-weight: 600; }
.se-status-cell.failed { color: #ef4444; font-weight: 600; }
.se-status-cell.cancelled, .se-status-cell.skipped { color: var(--fg-faint); }
.se-status-cell.running, .se-status-cell.exporting { color: var(--accent); }
.se-error {
font-family: var(--mono); font-size: 11px; color: #ef4444;
white-space: normal; word-break: break-word; margin-top: 2px;
}

/* scrollbars */
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
Expand Down
188 changes: 172 additions & 16 deletions src/ui/app.js
Loading