Release / publish (, ubuntu-22.04) (push) Failing after 14m39s
Release / publish (--target aarch64-apple-darwin, macos-latest) (push) Canceled after 0s
Release / publish (--target x86_64-apple-darwin, macos-15-intel) (push) Canceled after 0s
Release / publish (, windows-latest) (push) Canceled after 0s
* feat(models): Change::RebuildTable + role/privilege/readiness/maintenance structs (Task 1.1) * feat(introspection): role/privilege/tablespace/rebuild-readiness query builders (Task 1.2) * feat(lib): rebuild_table payload, table/role object kinds, v0.7.7 capabilities + types (Task 1.3) * feat(object_crud): table_ddl builder (create/edit-diff/options/drop) + build_ddl table arm (Task 2.1) * feat(object_crud): role_ddl builder (create/edit/drop/grant/revoke) + build_ddl role arm (Task 2.2) * feat(object_crud): rebuild_script assembler + fk/grant/sequence introspection queries (Task 2.3) * feat(commands): transactional RebuildTable arm + build_rebuild_script + get_table_columns commands (Task 2.4) * feat(commands): get_roles/privileges/readiness/tablespaces + run_maintenance (Task 2.5) * feat(commands): role/privilege/rebuild/maintenance typed wrappers (Task 3.1) * feat(store): roles slice + openFormTab table/role dedup (Task 3.2) * feat(ui): TableForm (create/edit-diff/rebuild) + cross-schema FK with ON DELETE/UPDATE (Task 4.1) * feat(ui): RoleForm + RoleDetail + RoleGrantsEditor (Task 4.2) * feat(ui): wire roles into object registry + explorer (Task 4.3) * feat(ui): Edit Table + maintenance menu + Create Table toolbar + rebuild badge (Task 4.4) * chore: bump version to 0.7.7 (Task 5.1) * docs: sync README/ROADMAP/AGENTS to v0.7.7 (Task 5.2) * test: v0.7.7 manual smoke checklist (Task 5.3) * chore: sync Cargo.lock to v0.7.7 * style(ui): match TableForm visual layout to shared object-form rows (remove stray padding) * feat(ui): table creator grid redesign + per-column FK slide-in panel with relationship controls * style(ui): table-creator columns grid — bordered cells like the data table view * feat(ui): table creator — type dropdown, PK/Nullable rules, drag-to-reorder columns (vertical), tidy add-column row * feat(ui): table creator — single-PK exclusivity, name on top, fixed column widths + contained h-scroll, no-wrap constraints * style(ui): table creator — constraints column sized to content (no clip), overscroll containment * style(ui): table creator — overscrollBehavior none on both scroll containers, add-column row py-2 * feat(ui): table creator — centered add-column button, schema dropdown auto-selected to current schema * style(ui): table creator — columns grid fills container width, scrolls only when too narrow * feat(ui): table creator — shorthand PG types in dropdown, Auto-Increment only for int-family * feat(ui): FK panel — animate in/out from the right, flush create-table-style rows * fix(ui): FK panel — smooth slide (willChange + eased), local column options from the form grid * feat(ui): FK composer (multi-column, type preview, gated sections) + composite PK + constraints cog in actions col + auto-named FKs * fix(ui): FK panel blocks unnamed table with hint; Foreign keys section lists staged + DB FKs * style(ui): FK panel — transparent full-width selects, title↔select borders, 2-col column picker * style(ui): FK panel — vertical divider between local/ref columns * style(ui): FK panel — section title padding py-2 * style(ui): FK panel — remove row margins + select/text padding in column picker * style(ui): FK panel — flush column picker (no section padding), border-r + py-1 cells * style(ui): FK panel — cell padding + row borders on column picker (user polish) * feat: FK inline into CREATE TABLE (single staged change in create mode); touch up class * style(ui): hide FK icon on PK columns; fix grid header/row column alignment * feat(ui): SQL preview in table creator uses read-only Monaco editor * feat(ui): Foreign keys section — relation rows with icon + Edit/Remove actions * style(ui): FK relation row — label, icon, table on one line * style(ui): FormRow selects transparent like FK panel; test matches FK relation label * style(ui): form-row focus outline amber-400/20 (subtle) * style(ui): SQL view — remove px-4 py-3 wrapper padding * feat(ui): Create Table button label; auto tree refresh after schema-modifying commits; table name on staged ddl card * fix(roles): cast rolconnlimit::int8 to fix panic deserializing int4 into i64 * fix(privileges): cast privilege_type::text to avoid domain-array panic; aclexplode for sequences/schemas (role_sequence_grants removed in PG15, schema_privileges never existed) * feat(ui): role privileges sections collapsible (collapsed by default) with 5-entry preview + fade + Show more * feat(ui): role privileges — collapsed state shows first 5 with fade; expand reveals all * chore(test): drop unused fireEvent import in RoleDetail test * style(ui): hide chevron when section has 5 or fewer entries * docs: README/ROADMAP/AGENTS — v0.7.7 shipped details (roles/grants, table editor, FK composer, privilege explorer); ROADMAP adds v0.7.8 Next up (quick wins) * docs(readme): collapse Recent changes and Key features into details blocks * docs(readme): move hero caption under image; align download + comparison table columns * docs(readme): collapse only body content — keep Recent Changes / Key Features headings visible * docs(readme): per-subsection collapsibles with meaningful summary labels
98 lines
3.5 KiB
TypeScript
98 lines
3.5 KiB
TypeScript
import type { QueueItem } from "../stores/dbViewerStore";
|
|
|
|
/**
|
|
* Payload emitted for a single queued change, keyed to match the Rust
|
|
* `Change` enum variants (snake_case). Sent to `executeChange`.
|
|
*/
|
|
export type ChangePayload = Record<string, unknown>;
|
|
|
|
function j(v: unknown): string {
|
|
return JSON.stringify(v ?? {});
|
|
}
|
|
|
|
function q(s: string): string {
|
|
return `"${s.replace(/"/g, '""')}"`;
|
|
}
|
|
|
|
function lit(v: unknown): string {
|
|
if (v === null || v === undefined) return "NULL";
|
|
if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
|
|
if (typeof v === "number") return String(v);
|
|
if (typeof v === "string") return `'${v.replace(/'/g, "''")}'`;
|
|
return `'${JSON.stringify(v).replace(/'/g, "''")}'`;
|
|
}
|
|
|
|
function tableRef(schema: string | undefined, table: string | undefined): string {
|
|
if (!table) return "-";
|
|
return schema ? `${q(schema)}.${q(table)}` : q(table);
|
|
}
|
|
|
|
export function buildChangeSql(item: QueueItem): string {
|
|
const t = tableRef(item.schema, item.table);
|
|
switch (item.type) {
|
|
case "insert": {
|
|
const data = item.newData ?? {};
|
|
const cols = Object.keys(data);
|
|
return `INSERT INTO ${t} (${cols.map(q).join(", ")}) VALUES (${cols.map((c) => lit(data[c])).join(", ")})`;
|
|
}
|
|
case "update": {
|
|
const data = item.newData ?? {};
|
|
const pk = item.primaryKey ?? {};
|
|
const setClause = Object.keys(data).map((c) => `${q(c)} = ${lit(data[c])}`).join(", ");
|
|
const whereClause = Object.keys(pk).map((c) => `${q(c)} = ${lit(pk[c])}`).join(" AND ");
|
|
return `UPDATE ${t} SET ${setClause} WHERE ${whereClause}`;
|
|
}
|
|
case "delete": {
|
|
const pk = item.primaryKey ?? {};
|
|
const whereClause = Object.keys(pk).map((c) => `${q(c)} = ${lit(pk[c])}`).join(" AND ");
|
|
return `DELETE FROM ${t} WHERE ${whereClause}`;
|
|
}
|
|
case "bulk_insert": {
|
|
const cols = item.columns ?? [];
|
|
const rows = item.rows ?? [];
|
|
const valueRows = rows
|
|
.map((row) => `(${row.map(lit).join(", ")})`)
|
|
.join(", ");
|
|
return `INSERT INTO ${t} (${cols.map(q).join(", ")}) VALUES ${valueRows}`;
|
|
}
|
|
case "empty_table":
|
|
return `DELETE FROM ${t}`;
|
|
case "ddl":
|
|
return item.sql ?? "";
|
|
case "rebuild_table":
|
|
return item.sql ?? "";
|
|
case "drop_table":
|
|
return `DROP TABLE ${t}`;
|
|
default:
|
|
return item.sql ?? "";
|
|
}
|
|
}
|
|
|
|
export function buildChangePayload(item: QueueItem): ChangePayload {
|
|
const schema = item.schema ?? "";
|
|
const table = item.table ?? "";
|
|
switch (item.type) {
|
|
case "insert":
|
|
return { id: item.id, type: "insert", schema, table, data: j(item.newData) };
|
|
case "update":
|
|
return { id: item.id, type: "update", schema, table,
|
|
primary_key: j(item.primaryKey), old_data: j(item.oldData), new_data: j(item.newData) };
|
|
case "delete":
|
|
return { id: item.id, type: "delete", schema, table, primary_key: j(item.primaryKey) };
|
|
case "alter_table":
|
|
return { id: item.id, type: "alter_table", schema, table, sql: item.sql, rollback_sql: "" };
|
|
case "bulk_insert":
|
|
return { id: item.id, type: "bulk_insert", schema, table,
|
|
columns: item.columns ?? [], rows: item.rows ?? [] };
|
|
case "drop_table":
|
|
return { id: item.id, type: "drop_table", schema, table };
|
|
case "empty_table":
|
|
return { id: item.id, type: "empty_table", schema, table };
|
|
case "ddl":
|
|
return { id: item.id, type: "ddl", sql: item.sql };
|
|
case "rebuild_table":
|
|
return { id: item.id, type: "rebuild_table", sql: item.sql };
|
|
default:
|
|
return { id: item.id, type: item.type, sql: item.sql };
|
|
}
|
|
} |