mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-30 08:20:33 +00:00
fix(hive): gate row edits by transactional support
This commit is contained in:
@@ -68,7 +68,12 @@ import {
|
||||
normalizeWhereInput,
|
||||
quoteTableIdentifier,
|
||||
} from "@/lib/tableSelectSql";
|
||||
import { isHiddenGridColumn, usesSyntheticRowIdKey } from "@/lib/tableEditing";
|
||||
import {
|
||||
canEditExistingTableRows,
|
||||
hiveTablePropertiesIndicateTransactional,
|
||||
isHiddenGridColumn,
|
||||
usesSyntheticRowIdKey,
|
||||
} from "@/lib/tableEditing";
|
||||
import { formatGridSqlLiteral } from "@/lib/dataGridSql";
|
||||
import { matchesRowStatusFilter, type RowStatus, type RowStatusFilter } from "@/lib/gridRowStatus";
|
||||
import { displayCellValue, type CellValue } from "@/lib/cellValue";
|
||||
@@ -806,6 +811,34 @@ const canUseWhereSearch = computed(() => !!props.tableMeta && !!props.onExecuteS
|
||||
const tableUsesSyntheticRowId = computed(() =>
|
||||
usesSyntheticRowIdKey(props.databaseType, props.tableMeta?.primaryKeys ?? []),
|
||||
);
|
||||
const hiveTableTransactional = ref<boolean | undefined>(undefined);
|
||||
const canEditExistingRows = computed(() => canEditExistingTableRows(props.databaseType, hiveTableTransactional.value));
|
||||
watch(
|
||||
() => [props.databaseType, props.connectionId, props.database, props.tableMeta?.schema, props.tableMeta?.tableName],
|
||||
async () => {
|
||||
if (props.databaseType !== "hive" || !props.connectionId || !props.database || !props.tableMeta) {
|
||||
hiveTableTransactional.value = undefined;
|
||||
return;
|
||||
}
|
||||
const table = qualifiedTableName({
|
||||
databaseType: "hive",
|
||||
schema: props.tableMeta.schema,
|
||||
tableName: props.tableMeta.tableName,
|
||||
});
|
||||
try {
|
||||
const result = await api.executeQuery(
|
||||
props.connectionId,
|
||||
props.database,
|
||||
`SHOW TBLPROPERTIES ${table} ('transactional')`,
|
||||
props.tableMeta.schema,
|
||||
);
|
||||
hiveTableTransactional.value = hiveTablePropertiesIndicateTransactional(result);
|
||||
} catch {
|
||||
hiveTableTransactional.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
const clientSearchText = computed(() => (searchText.value.trim() ? searchText.value : ""));
|
||||
watch(clientSearchText, (value) => {
|
||||
clearTimeout(_searchTimer);
|
||||
@@ -899,6 +932,7 @@ const editor = useDataGridEditor({
|
||||
connectionId: computed(() => props.connectionId),
|
||||
database: computed(() => props.database),
|
||||
tableMeta: computed(() => props.tableMeta),
|
||||
canEditExistingRows,
|
||||
onExecuteSql: computed(() => props.onExecuteSql),
|
||||
customSave: computed(() => props.customSave),
|
||||
sql: computed(() => props.sql),
|
||||
@@ -952,6 +986,14 @@ const {
|
||||
cleanupFrames,
|
||||
} = editor;
|
||||
|
||||
function canEditRowItem(item: RowItem | undefined): boolean {
|
||||
return !!props.editable && !!item && !item.isDeleted && (item.isNew || canEditExistingRows.value);
|
||||
}
|
||||
|
||||
function canDeleteRowItem(item: RowItem | undefined): boolean {
|
||||
return !!props.editable && !!item && !item.isDeleted && (item.isNew || canEditExistingRows.value);
|
||||
}
|
||||
|
||||
async function onToolbarRefresh() {
|
||||
if (transactionActive.value) {
|
||||
discardChanges();
|
||||
@@ -1186,7 +1228,7 @@ const activeCellDetail = computed(() => {
|
||||
rawValue,
|
||||
length: value === null ? 0 : String(value).length,
|
||||
formattedJson,
|
||||
isEditable: props.editable && !item.isDeleted,
|
||||
isEditable: canEditRowItem(item),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1216,6 +1258,7 @@ function commitDetailEdit() {
|
||||
}
|
||||
|
||||
if (item.sourceIndex === undefined) return;
|
||||
if (!canEditExistingRows.value) return;
|
||||
|
||||
const oldVal = props.result.rows[item.sourceIndex]?.[detail.colIndex];
|
||||
const newVal = coerceCellValue(detailEditValue.value, oldVal);
|
||||
@@ -1253,6 +1296,7 @@ function setDetailNull() {
|
||||
}
|
||||
|
||||
if (item.sourceIndex === undefined) return;
|
||||
if (!canEditExistingRows.value) return;
|
||||
if (!dirtyRows.value.has(item.sourceIndex)) dirtyRows.value.set(item.sourceIndex, new Map());
|
||||
dirtyRows.value.get(item.sourceIndex)!.set(detail.colIndex, null);
|
||||
dirtyRows.value = new Map(dirtyRows.value);
|
||||
@@ -2331,12 +2375,12 @@ defineExpose({
|
||||
'bg-yellow-500/10': item.isDirtyCol[actualColIdx],
|
||||
'cell-selected': cellIsSelected(index, visibleColIdx),
|
||||
'tabular-nums': typeof item.data[actualColIdx] === 'number',
|
||||
'cursor-text hover:bg-accent/50': editable && !item.isDeleted,
|
||||
'cursor-text hover:bg-accent/50': canEditRowItem(item),
|
||||
'line-through': item.isDeleted,
|
||||
}"
|
||||
@mousedown="handleDataCellMousedown(index, visibleColIdx, item.id, $event)"
|
||||
@mouseenter="extendCellSelection(index, visibleColIdx)"
|
||||
@dblclick="editable && !item.isDeleted && startEdit(item.id, actualColIdx)"
|
||||
@dblclick="canEditRowItem(item) && startEdit(item.id, actualColIdx)"
|
||||
@contextmenu="onCellContext(item.id, index, actualColIdx, visibleColIdx)"
|
||||
>
|
||||
<template v-if="editingCell?.rowId === item.id && editingCell?.col === actualColIdx">
|
||||
@@ -2664,7 +2708,7 @@ defineExpose({
|
||||
{{ isMultiRow ? t("grid.restoreRows", { count: multiRowCount }) : t("grid.restoreRow") }}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
v-else
|
||||
v-else-if="canDeleteRowItem(contextRowItem)"
|
||||
class="text-destructive"
|
||||
@click="isMultiRow ? requestDeleteRows(affectedRowIds()) : requestDeleteRow(contextRowItem.id)"
|
||||
>
|
||||
|
||||
@@ -50,6 +50,7 @@ export interface UseDataGridEditorOptions {
|
||||
}
|
||||
| undefined
|
||||
>;
|
||||
canEditExistingRows?: ComputedRef<boolean>;
|
||||
onExecuteSql: ComputedRef<((sql: string) => Promise<void>) | undefined>;
|
||||
customSave?: ComputedRef<
|
||||
| ((changes: {
|
||||
@@ -94,6 +95,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
connectionId,
|
||||
database,
|
||||
tableMeta,
|
||||
canEditExistingRows = computed(() => true),
|
||||
onExecuteSql,
|
||||
customSave,
|
||||
sql,
|
||||
@@ -256,6 +258,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
if (!editable.value) return;
|
||||
const item = getRowItem(rowId);
|
||||
if (!item || item.isDeleted) return;
|
||||
if (!item.isNew && !canEditExistingRows.value) return;
|
||||
isCancelling = false;
|
||||
editingCell.value = { rowId, col: colIdx };
|
||||
const val = item?.data[colIdx] ?? null;
|
||||
@@ -291,6 +294,10 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
editingCell.value = null;
|
||||
return;
|
||||
}
|
||||
if (!canEditExistingRows.value) {
|
||||
editingCell.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const oldVal = result.value.rows[item.sourceIndex]?.[col];
|
||||
const newVal = coerceCellValue(editValue.value, oldVal);
|
||||
@@ -320,6 +327,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
}
|
||||
|
||||
if (item.sourceIndex === undefined) return;
|
||||
if (!canEditExistingRows.value) return;
|
||||
|
||||
const oldVal = result.value.rows[item.sourceIndex]?.[col];
|
||||
const newVal = value === null ? null : coerceCellValue(value, oldVal);
|
||||
@@ -410,6 +418,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
if (item.isNew && item.newIndex !== undefined) {
|
||||
newRows.value.splice(item.newIndex, 1);
|
||||
} else if (item.sourceIndex !== undefined) {
|
||||
if (!canEditExistingRows.value) return;
|
||||
dirtyRows.value.delete(item.sourceIndex);
|
||||
deletedRows.value.add(item.sourceIndex);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,26 @@ export function supportsDataGridTransaction(databaseType: DatabaseType | undefin
|
||||
return databaseType !== "hive";
|
||||
}
|
||||
|
||||
export function canEditExistingTableRows(
|
||||
databaseType: DatabaseType | undefined,
|
||||
hiveTableTransactional?: boolean,
|
||||
): boolean {
|
||||
if (databaseType === "hive") return hiveTableTransactional === true;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function hiveTablePropertiesIndicateTransactional(result: { rows: readonly (readonly unknown[])[] }): boolean {
|
||||
return result.rows.some((row) => {
|
||||
const name = String(row[0] ?? "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const value = String(row[1] ?? "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
return name === "transactional" && value === "true";
|
||||
});
|
||||
}
|
||||
|
||||
export function usesSyntheticRowIdKey(databaseType: DatabaseType | undefined, primaryKeys: string[]): boolean {
|
||||
return (
|
||||
primaryKeys.length === 1 &&
|
||||
|
||||
Reference in New Issue
Block a user