mirror of
https://github.com/chartdb/chartdb.git
synced 2026-01-08 04:40:01 -06:00
feat(postgres): add support hash index types (#812)
* feat(postgres): add support for hash index type with single column constraint * some fixes * some fixes --------- Co-authored-by: Guy Ben-Aharon <baguy3@gmail.com>
This commit is contained in:
@@ -18,7 +18,7 @@ import {
|
||||
import { useStorage } from '@/hooks/use-storage';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { filterTable } from '@/lib/domain/diagram-filter/filter';
|
||||
import { schemaNameToSchemaId } from '@/lib/domain';
|
||||
import { databasesWithSchemas, schemaNameToSchemaId } from '@/lib/domain';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
import type { ChartDBEvent } from '../chartdb-context/chartdb-context';
|
||||
|
||||
@@ -246,7 +246,7 @@ export const DiagramFilterProvider: React.FC<React.PropsWithChildren> = ({
|
||||
const toggleTableFilter: DiagramFilterContext['toggleTableFilter'] =
|
||||
useCallback(
|
||||
(tableId: string) => {
|
||||
if (!defaultSchemas[databaseType]) {
|
||||
if (!databasesWithSchemas.includes(databaseType)) {
|
||||
// No schemas, toggle table filter without schema context
|
||||
toggleTableFilterForNoSchema(tableId);
|
||||
return;
|
||||
|
||||
@@ -149,6 +149,7 @@ export const ar: LanguageTranslation = {
|
||||
title: 'خصائص الفهرس',
|
||||
name: 'الإسم',
|
||||
unique: 'فريد',
|
||||
index_type: 'نوع الفهرس',
|
||||
delete_index: 'حذف الفهرس',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -151,6 +151,7 @@ export const bn: LanguageTranslation = {
|
||||
title: 'ইনডেক্স কর্ম',
|
||||
name: 'নাম',
|
||||
unique: 'অদ্বিতীয়',
|
||||
index_type: 'ইনডেক্স ধরন',
|
||||
delete_index: 'ইনডেক্স মুছুন',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -152,6 +152,7 @@ export const de: LanguageTranslation = {
|
||||
title: 'Indexattribute',
|
||||
name: 'Name',
|
||||
unique: 'Eindeutig',
|
||||
index_type: 'Indextyp',
|
||||
delete_index: 'Index löschen',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -145,6 +145,7 @@ export const en = {
|
||||
title: 'Index Attributes',
|
||||
name: 'Name',
|
||||
unique: 'Unique',
|
||||
index_type: 'Index Type',
|
||||
delete_index: 'Delete Index',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -150,6 +150,7 @@ export const es: LanguageTranslation = {
|
||||
title: 'Atributos del Índice',
|
||||
name: 'Nombre',
|
||||
unique: 'Único',
|
||||
index_type: 'Tipo de Índice',
|
||||
delete_index: 'Eliminar Índice',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -148,6 +148,7 @@ export const fr: LanguageTranslation = {
|
||||
title: "Attributs de l'Index",
|
||||
name: 'Nom',
|
||||
unique: 'Unique',
|
||||
index_type: "Type d'index",
|
||||
delete_index: "Supprimer l'Index",
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -152,6 +152,7 @@ export const gu: LanguageTranslation = {
|
||||
title: 'ઇન્ડેક્સ લક્ષણો',
|
||||
name: 'નામ',
|
||||
unique: 'અદ્વિતીય',
|
||||
index_type: 'ઇન્ડેક્સ પ્રકાર',
|
||||
delete_index: 'ઇન્ડેક્સ કાઢી નાખો',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -151,6 +151,7 @@ export const hi: LanguageTranslation = {
|
||||
title: 'सूचकांक विशेषताएँ',
|
||||
name: 'नाम',
|
||||
unique: 'अद्वितीय',
|
||||
index_type: 'इंडेक्स प्रकार',
|
||||
delete_index: 'सूचकांक हटाएँ',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -146,6 +146,7 @@ export const hr: LanguageTranslation = {
|
||||
title: 'Atributi indeksa',
|
||||
name: 'Naziv',
|
||||
unique: 'Jedinstven',
|
||||
index_type: 'Vrsta indeksa',
|
||||
delete_index: 'Izbriši indeks',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -150,6 +150,7 @@ export const id_ID: LanguageTranslation = {
|
||||
title: 'Atribut Indeks',
|
||||
name: 'Nama',
|
||||
unique: 'Unik',
|
||||
index_type: 'Tipe Indeks',
|
||||
delete_index: 'Hapus Indeks',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -154,6 +154,7 @@ export const ja: LanguageTranslation = {
|
||||
title: 'インデックス属性',
|
||||
name: '名前',
|
||||
unique: 'ユニーク',
|
||||
index_type: 'インデックスタイプ',
|
||||
delete_index: 'インデックスを削除',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -150,6 +150,7 @@ export const ko_KR: LanguageTranslation = {
|
||||
title: '인덱스 속성',
|
||||
name: '인덱스 명',
|
||||
unique: '유니크 여부',
|
||||
index_type: '인덱스 타입',
|
||||
delete_index: '인덱스 삭제',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -153,6 +153,7 @@ export const mr: LanguageTranslation = {
|
||||
title: 'इंडेक्स गुणधर्म',
|
||||
name: 'नाव',
|
||||
unique: 'युनिक',
|
||||
index_type: 'इंडेक्स प्रकार',
|
||||
delete_index: 'इंडेक्स हटवा',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -151,6 +151,7 @@ export const ne: LanguageTranslation = {
|
||||
title: 'सूचक विशेषताहरू',
|
||||
name: 'नाम',
|
||||
unique: 'अनन्य',
|
||||
index_type: 'इन्डेक्स प्रकार',
|
||||
delete_index: 'सूचक हटाउनुहोस्',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -151,6 +151,7 @@ export const pt_BR: LanguageTranslation = {
|
||||
title: 'Atributos do Índice',
|
||||
name: 'Nome',
|
||||
unique: 'Único',
|
||||
index_type: 'Tipo de Índice',
|
||||
delete_index: 'Excluir Índice',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -147,6 +147,7 @@ export const ru: LanguageTranslation = {
|
||||
title: 'Атрибуты индекса',
|
||||
name: 'Имя',
|
||||
unique: 'Уникальный',
|
||||
index_type: 'Тип индекса',
|
||||
delete_index: 'Удалить индекс',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -151,6 +151,7 @@ export const te: LanguageTranslation = {
|
||||
title: 'ఇండెక్స్ గుణాలు',
|
||||
name: 'పేరు',
|
||||
unique: 'అద్వితీయ',
|
||||
index_type: 'ఇండెక్స్ రకం',
|
||||
delete_index: 'ఇండెక్స్ తొలగించు',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -150,6 +150,7 @@ export const tr: LanguageTranslation = {
|
||||
title: 'İndeks Özellikleri',
|
||||
name: 'Ad',
|
||||
unique: 'Tekil',
|
||||
index_type: 'İndeks Türü',
|
||||
delete_index: 'İndeksi Sil',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -149,6 +149,7 @@ export const uk: LanguageTranslation = {
|
||||
title: 'Атрибути індексу',
|
||||
name: 'Назва індекса',
|
||||
unique: 'Унікальний',
|
||||
index_type: 'Тип індексу',
|
||||
delete_index: 'Видалити індекс',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -150,6 +150,7 @@ export const vi: LanguageTranslation = {
|
||||
title: 'Thuộc tính chỉ mục',
|
||||
name: 'Tên',
|
||||
unique: 'Giá trị duy nhất',
|
||||
index_type: 'Loại chỉ mục',
|
||||
delete_index: 'Xóa chỉ mục',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -147,6 +147,7 @@ export const zh_CN: LanguageTranslation = {
|
||||
title: '索引属性',
|
||||
name: '名称',
|
||||
unique: '唯一',
|
||||
index_type: '索引类型',
|
||||
delete_index: '删除索引',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -147,6 +147,7 @@ export const zh_TW: LanguageTranslation = {
|
||||
title: '索引屬性',
|
||||
name: '名稱',
|
||||
unique: '唯一',
|
||||
index_type: '索引類型',
|
||||
delete_index: '刪除索引',
|
||||
},
|
||||
table_actions: {
|
||||
|
||||
@@ -405,7 +405,7 @@ export function exportPostgreSQL({
|
||||
.filter(Boolean);
|
||||
|
||||
return indexFieldNames.length > 0
|
||||
? `CREATE ${index.unique ? 'UNIQUE ' : ''}INDEX ${indexName} ON ${tableName} (${indexFieldNames.join(', ')});`
|
||||
? `CREATE ${index.unique ? 'UNIQUE ' : ''}INDEX ${indexName} ON ${tableName}${index.type && index.type !== 'btree' ? ` USING ${index.type.toUpperCase()}` : ''} (${indexFieldNames.join(', ')});`
|
||||
: '';
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
@@ -2,6 +2,17 @@ import { z } from 'zod';
|
||||
import type { AggregatedIndexInfo } from '../data/import-metadata/metadata-types/index-info';
|
||||
import { generateId } from '../utils';
|
||||
import type { DBField } from './db-field';
|
||||
import { DatabaseType } from './database-type';
|
||||
|
||||
export const INDEX_TYPES = [
|
||||
'btree',
|
||||
'hash',
|
||||
'gist',
|
||||
'gin',
|
||||
'spgist',
|
||||
'brin',
|
||||
] as const;
|
||||
export type IndexType = (typeof INDEX_TYPES)[number];
|
||||
|
||||
export interface DBIndex {
|
||||
id: string;
|
||||
@@ -9,6 +20,7 @@ export interface DBIndex {
|
||||
unique: boolean;
|
||||
fieldIds: string[];
|
||||
createdAt: number;
|
||||
type?: IndexType | null;
|
||||
}
|
||||
|
||||
export const dbIndexSchema: z.ZodType<DBIndex> = z.object({
|
||||
@@ -17,6 +29,7 @@ export const dbIndexSchema: z.ZodType<DBIndex> = z.object({
|
||||
unique: z.boolean(),
|
||||
fieldIds: z.array(z.string()),
|
||||
createdAt: z.number(),
|
||||
type: z.enum(INDEX_TYPES).optional(),
|
||||
});
|
||||
|
||||
export const createIndexesFromMetadata = ({
|
||||
@@ -36,5 +49,10 @@ export const createIndexesFromMetadata = ({
|
||||
.map((c) => fields.find((f) => f.name === c.name)?.id)
|
||||
.filter((id): id is string => id !== undefined),
|
||||
createdAt: Date.now(),
|
||||
type: idx.index_type?.toLowerCase() as IndexType,
|
||||
})
|
||||
);
|
||||
|
||||
export const databaseIndexTypes: { [key in DatabaseType]?: IndexType[] } = {
|
||||
[DatabaseType.POSTGRESQL]: ['btree', 'hash'],
|
||||
};
|
||||
|
||||
@@ -16,7 +16,6 @@ import type { TreeNode } from '@/components/tree-view/tree';
|
||||
import { ScrollArea } from '@/components/scroll-area/scroll-area';
|
||||
import { useDiagramFilter } from '@/context/diagram-filter-context/use-diagram-filter';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/toggle/toggle-group';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
import type {
|
||||
GroupingMode,
|
||||
NodeContext,
|
||||
@@ -26,6 +25,7 @@ import type {
|
||||
} from './types';
|
||||
import { generateTreeDataByAreas, generateTreeDataBySchemas } from './utils';
|
||||
import { FilterItemActions } from './filter-item-actions';
|
||||
import { databasesWithSchemas } from '@/lib/domain';
|
||||
|
||||
export interface CanvasFilterProps {
|
||||
onClose: () => void;
|
||||
@@ -63,7 +63,7 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
|
||||
);
|
||||
|
||||
const databaseWithSchemas = useMemo(
|
||||
() => !!defaultSchemas[databaseType],
|
||||
() => databasesWithSchemas.includes(databaseType),
|
||||
[databaseType]
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Ellipsis, Trash2 } from 'lucide-react';
|
||||
import { Button } from '@/components/button/button';
|
||||
import type { DBIndex } from '@/lib/domain/db-index';
|
||||
import {
|
||||
databaseIndexTypes,
|
||||
type DBIndex,
|
||||
type IndexType,
|
||||
} from '@/lib/domain/db-index';
|
||||
import type { DBField } from '@/lib/domain/db-field';
|
||||
import {
|
||||
Popover,
|
||||
@@ -20,6 +24,7 @@ import {
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/tooltip/tooltip';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
|
||||
export interface TableIndexProps {
|
||||
index: DBIndex;
|
||||
@@ -28,6 +33,11 @@ export interface TableIndexProps {
|
||||
fields: DBField[];
|
||||
}
|
||||
|
||||
const allIndexTypeOptions: { label: string; value: IndexType }[] = [
|
||||
{ label: 'B-tree (default)', value: 'btree' },
|
||||
{ label: 'Hash', value: 'hash' },
|
||||
];
|
||||
|
||||
export const TableIndex: React.FC<TableIndexProps> = ({
|
||||
fields,
|
||||
index,
|
||||
@@ -35,14 +45,51 @@ export const TableIndex: React.FC<TableIndexProps> = ({
|
||||
removeIndex,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { databaseType } = useChartDB();
|
||||
const fieldOptions = fields.map((field) => ({
|
||||
label: field.name,
|
||||
value: field.id,
|
||||
}));
|
||||
const updateIndexFields = (fieldIds: string | string[]) => {
|
||||
const ids = Array.isArray(fieldIds) ? fieldIds : [fieldIds];
|
||||
updateIndex({ fieldIds: ids });
|
||||
};
|
||||
const updateIndexFields = useCallback(
|
||||
(fieldIds: string | string[]) => {
|
||||
const ids = Array.isArray(fieldIds) ? fieldIds : [fieldIds];
|
||||
|
||||
// For hash indexes, only keep the last selected field
|
||||
if (index.type === 'hash' && ids.length > 0) {
|
||||
updateIndex({ fieldIds: [ids[ids.length - 1]] });
|
||||
} else {
|
||||
updateIndex({ fieldIds: ids });
|
||||
}
|
||||
},
|
||||
[index.type, updateIndex]
|
||||
);
|
||||
|
||||
const indexTypeOptions = useMemo(
|
||||
() =>
|
||||
allIndexTypeOptions.filter((option) =>
|
||||
databaseIndexTypes[databaseType]?.includes(option.value)
|
||||
),
|
||||
[databaseType]
|
||||
);
|
||||
|
||||
const updateIndexType = useCallback(
|
||||
(value: string | string[]) => {
|
||||
{
|
||||
const newType = value as IndexType;
|
||||
// If switching to hash and multiple fields are selected, keep only the first
|
||||
if (newType === 'hash' && index.fieldIds.length > 1) {
|
||||
updateIndex({
|
||||
type: newType,
|
||||
fieldIds: [index.fieldIds[0]],
|
||||
});
|
||||
} else {
|
||||
updateIndex({ type: newType });
|
||||
}
|
||||
}
|
||||
},
|
||||
[updateIndex, index.fieldIds]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-row justify-between gap-2 p-1">
|
||||
<SelectBox
|
||||
@@ -135,6 +182,23 @@ export const TableIndex: React.FC<TableIndexProps> = ({
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{indexTypeOptions.length > 0 ? (
|
||||
<div className="mt-2 flex flex-col gap-2">
|
||||
<Label
|
||||
htmlFor="indexType"
|
||||
className="text-subtitle"
|
||||
>
|
||||
{t(
|
||||
'side_panel.tables_section.table.index_actions.index_type'
|
||||
)}
|
||||
</Label>
|
||||
<SelectBox
|
||||
options={indexTypeOptions}
|
||||
value={index.type || 'btree'}
|
||||
onChange={updateIndexType}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<Separator orientation="horizontal" />
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
Reference in New Issue
Block a user