diff --git a/src/containers/Tenant/Diagnostics/Diagnostics.tsx b/src/containers/Tenant/Diagnostics/Diagnostics.tsx index e3141e7c0a..26aaef7871 100644 --- a/src/containers/Tenant/Diagnostics/Diagnostics.tsx +++ b/src/containers/Tenant/Diagnostics/Diagnostics.tsx @@ -104,7 +104,14 @@ function Diagnostics(props: DiagnosticsProps) { ); } case TENANT_DIAGNOSTICS_TABS_IDS.schema: { - return ; + return ( + + ); } case TENANT_DIAGNOSTICS_TABS_IDS.topQueries: { return ; diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx index b824af56a4..91b02a4c66 100644 --- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx +++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx @@ -45,7 +45,7 @@ import { PaneVisibilityToggleButtons, paneVisibilityToggleReducerCreator, } from '../utils/paneVisibilityToggleHelpers'; -import {isIndexTableType, isTableType, isViewType} from '../utils/schema'; +import {isIndexTableType, isTableType} from '../utils/schema'; import './ObjectSummary.scss'; @@ -64,6 +64,7 @@ const getTenantCommonInfoState = () => { interface ObjectSummaryProps { type?: EPathType; subType?: EPathSubType; + tenantName?: string; onCollapseSummary: VoidFunction; onExpandSummary: VoidFunction; isCollapsed: boolean; @@ -72,6 +73,7 @@ interface ObjectSummaryProps { export function ObjectSummary({ type, subType, + tenantName, onCollapseSummary, onExpandSummary, isCollapsed, @@ -97,15 +99,12 @@ export function ObjectSummary({ ignoreQueryPrefix: true, }); - const {name: tenantName} = queryParams; - const pathData = tenantName ? data[tenantName.toString()]?.PathDescription?.Self : undefined; const currentObjectData = currentSchemaPath ? data[currentSchemaPath] : undefined; const currentSchemaData = currentObjectData?.PathDescription?.Self; React.useEffect(() => { - // TODO: enable schema tab for view when supported - const isTable = isTableType(type) && !isViewType(type); + const isTable = isTableType(type); if (type && !isTable && !TENANT_INFO_TABS.find((el) => el.id === summaryTab)) { dispatch(setSummaryTab(TENANT_SUMMARY_TABS_IDS.overview)); @@ -113,8 +112,7 @@ export function ObjectSummary({ }, [dispatch, type, summaryTab]); const renderTabs = () => { - // TODO: enable schema tab for view when supported - const isTable = isTableType(type) && !isViewType(type); + const isTable = isTableType(type); const tabsItems = isTable ? [...TENANT_INFO_TABS, ...TENANT_SCHEMA_TAB] : TENANT_INFO_TABS; return ( @@ -126,7 +124,7 @@ export function ObjectSummary({ wrapTo={({id}, node) => { const path = createHref(routes.tenant, undefined, { ...queryParams, - name: tenantName as string, + name: tenantName, [TenantTabsGroups.summaryTab]: id, }); return ( @@ -218,7 +216,9 @@ export function ObjectSummary({ return ; } case TENANT_SUMMARY_TABS_IDS.schema: { - return ; + return ( + + ); } default: { return renderObjectOverview(); diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 39d1c64dc8..a3e30a2149 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -1,59 +1,94 @@ +import React from 'react'; + import DataTable from '@gravity-ui/react-data-table'; +import {skipToken} from '@reduxjs/toolkit/query'; import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable'; import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton'; +import {viewSchemaApi} from '../../../../store/reducers/viewSchema/viewSchema'; import type {EPathType} from '../../../../types/api/schema'; -import {cn} from '../../../../utils/cn'; import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants'; import {useTypedSelector} from '../../../../utils/hooks'; +import { + isColumnEntityType, + isExternalTableType, + isRowTableType, + isViewType, +} from '../../utils/schema'; import { SCHEMA_COLUMNS_WIDTH_LS_KEY, - SchemaViewerColumns, - prepareColumnDescriptions, - prepareFamilies, - prepareSchemaTableColumns, -} from './helpers'; + SCHEMA_TABLE_COLUMS_IDS, + getColumnTableColumns, + getExternalTableColumns, + getRowTableColumns, + getViewColumns, +} from './columns'; +import {prepareSchemaData, prepareViewSchema} from './prepareData'; +import {b} from './shared'; import './SchemaViewer.scss'; -const b = cn('schema-viewer'); - interface SchemaViewerProps { type?: EPathType; path?: string; - withFamilies?: boolean; + tenantName?: string | null; + extended?: boolean; } -export const SchemaViewer = ({type, path, withFamilies = false}: SchemaViewerProps) => { - const {data, loading} = useTypedSelector((state) => state.schema); - const currentObjectData = path ? data[path] : undefined; - - const {columns, keyColumnIds} = prepareColumnDescriptions(type, currentObjectData); - const families = prepareFamilies(currentObjectData); - - return ( -
- {loading ? ( - - ) : ( - - )} -
- ); +export const SchemaViewer = ({type, path, tenantName, extended = false}: SchemaViewerProps) => { + const {data: schemaData, loading} = useTypedSelector((state) => state.schema); + const currentObjectData = path ? schemaData[path] : undefined; + + const viewSchemaRequestParams = + isViewType(type) && path && tenantName ? {path, database: tenantName} : skipToken; + + const {data: viewColumnsData, isLoading: isViewSchemaLoading} = + viewSchemaApi.useGetViewSchemaQuery(viewSchemaRequestParams); + + const tableData = React.useMemo(() => { + if (isViewType(type)) { + return prepareViewSchema(viewColumnsData); + } + + return prepareSchemaData(type, currentObjectData); + }, [currentObjectData, type, viewColumnsData]); + + const columns = React.useMemo(() => { + if (isViewType(type)) { + return getViewColumns(); + } + if (isExternalTableType(type)) { + return getExternalTableColumns(); + } + if (isColumnEntityType(type)) { + return getColumnTableColumns(); + } + if (isRowTableType(type)) { + return getRowTableColumns(extended); + } + + return []; + }, [type, extended]); + + const renderContent = () => { + if (loading || isViewSchemaLoading) { + return ; + } + + return ( + + ); + }; + + return
{renderContent()}
; }; diff --git a/src/containers/Tenant/Schema/SchemaViewer/columns.tsx b/src/containers/Tenant/Schema/SchemaViewer/columns.tsx new file mode 100644 index 0000000000..b967a6c9ce --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/columns.tsx @@ -0,0 +1,123 @@ +import DataTable from '@gravity-ui/react-data-table'; +import {Icon} from '@gravity-ui/uikit'; + +import i18n from './i18n'; +import {b} from './shared'; +import type {SchemaColumn, SchemaData} from './types'; + +import keyIcon from '../../../../assets/icons/key.svg'; + +export const SCHEMA_COLUMNS_WIDTH_LS_KEY = 'schemaTableColumnsWidth'; + +export const SCHEMA_TABLE_COLUMS_IDS = { + id: 'id', + name: 'name', + isKeyColumn: 'isKeyColumn', + type: 'type', + notNull: 'notNull', + familyName: 'familyName', + prefferedPoolKind: 'prefferedPoolKind', + columnCodec: 'columnCodec', +} satisfies Record; + +const idColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.id, + get header() { + return i18n('column-title.id'); + }, + width: 60, + render: ({row}) => row.id, +}; +const nameColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.name, + get header() { + return i18n('column-title.name'); + }, + width: 100, + render: ({row}) => row.name, +}; +const keyColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.isKeyColumn, + get header() { + return i18n('column-title.key'); + }, + width: 70, + resizeMinWidth: 70, + // Table should start with key columns on sort click + defaultOrder: DataTable.ASCENDING, + sortAccessor: (row) => row.keyAccessor, + render: ({row}) => { + return row.isKeyColumn ? ( +
+ +
+ ) : null; + }, +}; +const typeColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.type, + get header() { + return i18n('column-title.type'); + }, + width: 100, + render: ({row}) => row.type, +}; +const notNullColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.notNull, + get header() { + return i18n('column-title.notNull'); + }, + width: 100, + // Table should start with notNull columns on sort click + defaultOrder: DataTable.DESCENDING, + render: ({row}) => { + if (row.notNull) { + return '\u2713'; + } + + return undefined; + }, +}; +const familyColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.familyName, + get header() { + return i18n('column-title.family'); + }, + width: 100, + render: ({row}) => row.familyName, +}; +const mediaColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.prefferedPoolKind, + get header() { + return i18n('column-title.media'); + }, + width: 100, + render: ({row}) => row.prefferedPoolKind, +}; +const compressionColumn: SchemaColumn = { + name: SCHEMA_TABLE_COLUMS_IDS.columnCodec, + get header() { + return i18n('column-title.compression'); + }, + width: 100, + render: ({row}) => row.columnCodec, +}; + +export function getViewColumns(): SchemaColumn[] { + return [nameColumn, typeColumn]; +} +export function getExternalTableColumns(): SchemaColumn[] { + return [idColumn, nameColumn, typeColumn, notNullColumn]; +} +export function getColumnTableColumns(): SchemaColumn[] { + return [idColumn, keyColumn, nameColumn, typeColumn, notNullColumn]; +} +export function getRowTableColumns(extended: boolean): SchemaColumn[] { + const rowTableColumns = [idColumn, keyColumn, nameColumn, typeColumn, notNullColumn]; + + if (extended) { + return rowTableColumns.concat(familyColumn, mediaColumn, compressionColumn); + } + + return rowTableColumns; +} diff --git a/src/containers/Tenant/Schema/SchemaViewer/helpers.tsx b/src/containers/Tenant/Schema/SchemaViewer/helpers.tsx deleted file mode 100644 index d94c47bbd6..0000000000 --- a/src/containers/Tenant/Schema/SchemaViewer/helpers.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import type {ClassNameFormatter} from '@bem-react/classname'; -import DataTable from '@gravity-ui/react-data-table'; -import type {Column} from '@gravity-ui/react-data-table'; -import {Icon} from '@gravity-ui/uikit'; - -import type { - EPathType, - TColumnDescription, - TColumnTableDescription, - TEvDescribeSchemeResult, - TFamilyDescription, -} from '../../../../types/api/schema'; -import {EColumnCodec} from '../../../../types/api/schema'; -import { - isColumnEntityType, - isExternalTableType, - isRowTableType, - isTableType, -} from '../../utils/schema'; - -import keyIcon from '../../../../assets/icons/key.svg'; - -export const SchemaViewerColumns = { - id: 'Id', - name: 'Name', - key: 'Key', - type: 'Type', - notNull: 'NotNull', - familyName: 'Family', - preferredPoolKind: 'Media', - columnCodec: 'Compression', -}; - -function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) { - const {Name, Schema} = tableSchema; - - if (Schema) { - const {Columns, KeyColumnNames} = Schema; - const KeyColumnIds = KeyColumnNames?.map((name: string) => { - const column = Columns?.find((el) => el.Name === name); - return column?.Id; - }).filter((id): id is number => id !== undefined); - - return { - Columns, - KeyColumnNames, - Name, - KeyColumnIds, - }; - } - - return { - Name, - }; -} - -function formatColumnCodec(codec?: EColumnCodec) { - if (!codec) { - return null; - } - if (codec === EColumnCodec.ColumnCodecPlain) { - return 'None'; - } - return codec.replace('ColumnCodec', '').toLocaleLowerCase(); -} - -export function prepareColumnDescriptions( - type?: EPathType, - scheme?: TEvDescribeSchemeResult, -): {columns: TColumnDescription[]; keyColumnIds: number[]} { - let keyColumnIds: number[] = []; - let columns: TColumnDescription[] = []; - - if (isTableType(type) && isColumnEntityType(type)) { - const description = scheme?.PathDescription?.ColumnTableDescription; - const columnTableSchema = prepareOlapTableSchema(description); - keyColumnIds = columnTableSchema.KeyColumnIds ?? []; - columns = columnTableSchema.Columns ?? []; - } else if (isExternalTableType(type)) { - columns = scheme?.PathDescription?.ExternalTableDescription?.Columns ?? []; - } else { - keyColumnIds = scheme?.PathDescription?.Table?.KeyColumnIds ?? []; - columns = scheme?.PathDescription?.Table?.Columns ?? []; - } - - return {columns, keyColumnIds}; -} - -export function prepareFamilies( - scheme?: TEvDescribeSchemeResult, -): Record { - return ( - scheme?.PathDescription?.Table?.PartitionConfig?.ColumnFamilies?.reduce< - Record - >((acc, family) => { - if (family.Id) { - acc[family.Id] = family; - } - return acc; - }, {}) ?? {} - ); -} - -export const SCHEMA_COLUMNS_WIDTH_LS_KEY = 'schemaTableColumnsWidth'; - -export function prepareSchemaTableColumns(options: { - type?: EPathType; - b: ClassNameFormatter; - families: Record; - keyColumnIds: number[]; - withFamilies: boolean; -}): Column[] { - const keyColumnsOrderValues = options.keyColumnIds.reduce>( - (result, keyColumnId, index) => { - // Put columns with negative values, so they will be the first with ascending sort - // Minus keyColumnIds.length for the first key, -1 for the last - result[keyColumnId] = index - options.keyColumnIds.length; - return result; - }, - {}, - ); - - const columns: Column[] = [ - { - name: SchemaViewerColumns.id, - width: 60, - }, - ]; - - if (!isExternalTableType(options.type)) { - // External tables don't have key columns - columns.push({ - name: SchemaViewerColumns.key, - width: 70, - resizeMinWidth: 70, - // Table should start with key columns on sort click - defaultOrder: DataTable.ASCENDING, - // Values in keyColumnsOrderValues are always negative, so it will be 1 for not key columns - sortAccessor: (row) => (row.Id && keyColumnsOrderValues[row.Id]) || 1, - render: ({row}) => { - return row.Id && options.keyColumnIds.includes(row.Id) ? ( -
- -
- ) : null; - }, - }); - } - - columns.push( - { - name: SchemaViewerColumns.name, - width: 100, - }, - { - name: SchemaViewerColumns.type, - width: 100, - }, - { - name: SchemaViewerColumns.notNull, - width: 100, - // Table should start with notNull columns on sort click - defaultOrder: DataTable.DESCENDING, - render: ({row}) => { - if (row.NotNull) { - return '\u2713'; - } - - return undefined; - }, - }, - ); - - if (options.withFamilies && isRowTableType(options.type)) { - columns.push( - { - name: SchemaViewerColumns.familyName, - width: 100, - render: ({row}) => (row.Family ? options.families[row.Family].Name : undefined), - sortAccessor: (row) => (row.Family ? options.families[row.Family].Name : undefined), - }, - { - name: SchemaViewerColumns.preferredPoolKind, - width: 100, - render: ({row}) => - row.Family - ? options.families[row.Family].StorageConfig?.Data?.PreferredPoolKind - : undefined, - sortAccessor: (row) => - row.Family - ? options.families[row.Family].StorageConfig?.Data?.PreferredPoolKind - : undefined, - }, - { - name: SchemaViewerColumns.columnCodec, - width: 100, - render: ({row}) => - row.Family - ? formatColumnCodec(options.families[row.Family].ColumnCodec) - : undefined, - sortAccessor: (row) => - row.Family ? options.families[row.Family].ColumnCodec : undefined, - }, - ); - } - - return columns; -} diff --git a/src/containers/Tenant/Schema/SchemaViewer/i18n/en.json b/src/containers/Tenant/Schema/SchemaViewer/i18n/en.json new file mode 100644 index 0000000000..3ff93c5b91 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/i18n/en.json @@ -0,0 +1,10 @@ +{ + "column-title.id": "Id", + "column-title.name": "Name", + "column-title.key": "Key", + "column-title.type": "Type", + "column-title.notNull": "NotNull", + "column-title.family": "Family", + "column-title.media": "Media", + "column-title.compression": "Compression" +} diff --git a/src/containers/Tenant/Schema/SchemaViewer/i18n/index.ts b/src/containers/Tenant/Schema/SchemaViewer/i18n/index.ts new file mode 100644 index 0000000000..6f1751a296 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/i18n/index.ts @@ -0,0 +1,7 @@ +import {registerKeysets} from '../../../../../utils/i18n'; + +import en from './en.json'; + +const COMPONENT = 'ydb-schema-viewer'; + +export default registerKeysets(COMPONENT, {en}); diff --git a/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts b/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts new file mode 100644 index 0000000000..fc55a00052 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts @@ -0,0 +1,165 @@ +import type {ColumnType} from '../../../../types/api/query'; +import type { + EPathType, + TColumnTableDescription, + TEvDescribeSchemeResult, + TExternalTableDescription, + TFamilyDescription, + TTableDescription, +} from '../../../../types/api/schema'; +import {EColumnCodec} from '../../../../types/api/schema'; +import {isColumnEntityType, isExternalTableType, isRowTableType} from '../../utils/schema'; + +import type {SchemaData} from './types'; + +function getKeyColumnsSortAccessorMap(ids: T[] = []): Record { + return ids.reduce( + (result, keyColumnId, index) => { + // Put columns with negative values, so they will be the first with ascending sort + // Minus keyColumnIds.length for the first key, -1 for the last + return { + ...result, + [keyColumnId]: index - ids.length, + }; + }, + {} as Record, + ); +} + +function formatColumnCodec(codec?: EColumnCodec) { + if (!codec) { + return undefined; + } + if (codec === EColumnCodec.ColumnCodecPlain) { + return 'None'; + } + return codec.replace('ColumnCodec', '').toLocaleLowerCase(); +} + +export function prepareFamilies(data?: TTableDescription): Record { + return ( + data?.PartitionConfig?.ColumnFamilies?.reduce>( + (acc, family) => { + if (family.Id) { + return { + ...acc, + [family.Id]: family, + }; + } + return acc; + }, + {}, + ) ?? {} + ); +} + +function prepareRowTableSchema(data: TTableDescription = {}): SchemaData[] { + const families = prepareFamilies(data); + + const {Columns, KeyColumnIds} = data; + + const keyAccessorsMap = getKeyColumnsSortAccessorMap(KeyColumnIds); + + const preparedColumns = Columns?.map((column) => { + const {Id, Name, NotNull, Type, Family} = column; + + const isKeyColumn = Boolean(KeyColumnIds?.find((keyColumnId) => keyColumnId === Id)); + // Values in keyAccessorsMap are always negative, so it will be 1 for not key columns + const keyAccessor = Id && keyAccessorsMap[Id] ? keyAccessorsMap[Id] : 1; + + const familyName = Family ? families[Family].Name : undefined; + const prefferedPoolKind = Family + ? families[Family].StorageConfig?.Data?.PreferredPoolKind + : undefined; + const columnCodec = Family ? formatColumnCodec(families[Family].ColumnCodec) : undefined; + + return { + id: Id, + name: Name, + isKeyColumn, + keyAccessor, + type: Type, + notNull: NotNull, + familyName, + prefferedPoolKind, + columnCodec, + }; + }); + + return preparedColumns || []; +} + +function prepareExternalTableSchema(data: TExternalTableDescription = {}): SchemaData[] { + const {Columns} = data; + const preparedColumns = Columns?.map((column) => { + const {Id, Name, Type, NotNull} = column; + return { + id: Id, + name: Name, + type: Type, + notNull: NotNull, + }; + }); + + return preparedColumns || []; +} + +function prepareColumnTableSchema(data: TColumnTableDescription = {}): SchemaData[] { + const {Schema = {}} = data; + const {Columns, KeyColumnNames} = Schema; + + const keyAccessorsMap = getKeyColumnsSortAccessorMap(KeyColumnNames); + + const preparedColumns = Columns?.map((column) => { + const {Id, Name, Type, NotNull} = column; + + const isKeyColumn = Boolean( + KeyColumnNames?.find((keyColumnName) => keyColumnName === Name), + ); + + // Values in keyAccessorsMap are always negative, so it will be 1 for not key columns + const keyAccessor = Name && keyAccessorsMap[Name] ? keyAccessorsMap[Name] : 1; + + return { + id: Id, + name: Name, + isKeyColumn, + keyAccessor, + type: Type, + notNull: NotNull, + }; + }); + + return preparedColumns || []; +} + +export function prepareSchemaData( + type?: EPathType, + schema?: TEvDescribeSchemeResult, +): SchemaData[] { + const {Table, ColumnTableDescription, ExternalTableDescription} = schema?.PathDescription || {}; + + if (isRowTableType(type)) { + return prepareRowTableSchema(Table); + } else if (isColumnEntityType(type)) { + return prepareColumnTableSchema(ColumnTableDescription); + } else if (isExternalTableType(type)) { + return prepareExternalTableSchema(ExternalTableDescription); + } + + return []; +} + +export function prepareViewSchema(columns?: ColumnType[]): SchemaData[] { + const preparedColumns = columns?.map((column) => { + // View may have columns like Uint64? or Utf8? + const type = column.type?.endsWith('?') ? column.type.slice(0, -1) : column.type; + + return { + type, + name: column.name, + }; + }); + + return preparedColumns || []; +} diff --git a/src/containers/Tenant/Schema/SchemaViewer/shared.ts b/src/containers/Tenant/Schema/SchemaViewer/shared.ts new file mode 100644 index 0000000000..35a8bb45a4 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/shared.ts @@ -0,0 +1,3 @@ +import {cn} from '../../../../utils/cn'; + +export const b = cn('schema-viewer'); diff --git a/src/containers/Tenant/Schema/SchemaViewer/types.ts b/src/containers/Tenant/Schema/SchemaViewer/types.ts new file mode 100644 index 0000000000..2c820ad464 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/types.ts @@ -0,0 +1,18 @@ +import type {Column} from '@gravity-ui/react-data-table'; + +export interface SchemaData { + id?: number; + name?: string; + isKeyColumn?: boolean; + /** value to sort keys, needed to ensure that key columns will be the first */ + keyAccessor?: number; + type?: string; + notNull?: boolean; + familyName?: string; + prefferedPoolKind?: string; + columnCodec?: string; +} + +export interface SchemaColumn extends Column { + name: keyof SchemaData; +} diff --git a/src/containers/Tenant/Tenant.tsx b/src/containers/Tenant/Tenant.tsx index e14109403c..cf062c6ede 100644 --- a/src/containers/Tenant/Tenant.tsx +++ b/src/containers/Tenant/Tenant.tsx @@ -140,6 +140,7 @@ function Tenant(props: TenantProps) { ({ + getViewSchema: build.query({ + queryFn: async ({database, path}: {database: string; path: string}) => { + try { + const response = await window.api.sendQuery({ + schema: 'modern', + query: createViewSchemaQuery(path), + database, + action: 'execute-scan', + }); + + if (isQueryErrorResponse(response)) { + return {error: response}; + } + + return {data: response.columns || []}; + } catch (error) { + return {error: error}; + } + }, + providesTags: ['All'], + }), + }), + overrideExisting: 'throw', +}); diff --git a/src/types/api/schema/columnEntity.ts b/src/types/api/schema/columnEntity.ts index 070cd8f897..32459fe69b 100644 --- a/src/types/api/schema/columnEntity.ts +++ b/src/types/api/schema/columnEntity.ts @@ -95,6 +95,7 @@ interface TOlapColumnDescription { Id?: number; Name?: string; Type?: string; + NotNull?: boolean; TypeId?: number; TypeInfo?: TTypeInfo; }