diff --git a/src/containers/Tenant/Schema/SchemaViewer/__tests__/prepareData.test.ts b/src/containers/Tenant/Schema/SchemaViewer/__tests__/prepareData.test.ts new file mode 100644 index 000000000..01ecf1826 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/__tests__/prepareData.test.ts @@ -0,0 +1,272 @@ +import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema'; +import {EPathType} from '../../../../../types/api/schema'; +import {prepareSchemaData, prepareViewSchema} from '../prepareData'; + +describe('prepareSchemaData', () => { + it('correctly parses row table data', () => { + const data: TEvDescribeSchemeResult = { + PathDescription: { + Table: { + Name: 'my_row_table', + Columns: [ + { + Name: 'id', + Type: 'Uint64', + TypeId: 4, + Id: 1, + Family: 0, + FamilyName: 'default', + NotNull: false, + }, + { + Name: 'category_id', + Type: 'Uint64', + TypeId: 4, + Id: 2, + Family: 0, + FamilyName: 'default', + NotNull: true, + }, + { + Name: 'name', + Type: 'Utf8', + TypeId: 4608, + Id: 3, + Family: 0, + FamilyName: 'default', + DefaultFromLiteral: { + type: { + type_id: 'UTF8', + }, + value: { + text_value: 'Ivan', + }, + }, + NotNull: true, + }, + { + Name: 'updated_on', + Type: 'Datetime', + TypeId: 49, + Id: 4, + Family: 0, + FamilyName: 'default', + NotNull: false, + }, + ], + KeyColumnNames: ['category_id', 'name', 'id'], + KeyColumnIds: [2, 3, 1], + PartitionConfig: { + ColumnFamilies: [ + { + Id: 0, + StorageConfig: { + SysLog: { + PreferredPoolKind: 'ssd', + }, + Log: { + PreferredPoolKind: 'ssd', + }, + Data: { + PreferredPoolKind: 'ssd', + }, + }, + }, + ], + }, + }, + }, + }; + + const result = [ + { + id: 1, + name: 'id', + keyColumnIndex: 2, + type: 'Uint64', + notNull: false, + autoIncrement: false, + defaultValue: '-', + familyName: undefined, + prefferedPoolKind: undefined, + columnCodec: undefined, + }, + { + id: 2, + name: 'category_id', + keyColumnIndex: 0, + type: 'Uint64', + notNull: true, + autoIncrement: false, + defaultValue: '-', + familyName: undefined, + prefferedPoolKind: undefined, + columnCodec: undefined, + }, + { + id: 3, + name: 'name', + keyColumnIndex: 1, + type: 'Utf8', + notNull: true, + autoIncrement: false, + defaultValue: 'Ivan', + familyName: undefined, + prefferedPoolKind: undefined, + columnCodec: undefined, + }, + { + id: 4, + name: 'updated_on', + keyColumnIndex: -1, + type: 'Datetime', + notNull: false, + autoIncrement: false, + defaultValue: '-', + familyName: undefined, + prefferedPoolKind: undefined, + columnCodec: undefined, + }, + ]; + + expect(prepareSchemaData(EPathType.EPathTypeTable, data)).toEqual(result); + }); + it('correctly parses column table data', () => { + const data: TEvDescribeSchemeResult = { + PathDescription: { + ColumnTableDescription: { + Name: 'my_column_table', + Schema: { + Columns: [ + { + Id: 1, + Name: 'title', + Type: 'Utf8', + TypeId: 4608, + NotNull: true, + StorageId: '', + DefaultValue: {}, + ColumnFamilyId: 0, + }, + { + Id: 2, + Name: 'author', + Type: 'Utf8', + TypeId: 4608, + NotNull: true, + StorageId: '', + DefaultValue: {}, + ColumnFamilyId: 0, + }, + { + Id: 3, + Name: 'id', + Type: 'Int64', + TypeId: 3, + NotNull: true, + StorageId: '', + DefaultValue: {}, + ColumnFamilyId: 0, + }, + { + Id: 4, + Name: 'body', + Type: 'Utf8', + TypeId: 4608, + NotNull: false, + StorageId: '', + DefaultValue: {}, + ColumnFamilyId: 0, + }, + ], + KeyColumnNames: ['author', 'title', 'id'], + Version: '1', + Options: { + SchemeNeedActualization: false, + }, + ColumnFamilies: [ + { + Id: 0, + Name: 'default', + }, + ], + }, + ColumnShardCount: 64, + Sharding: { + ColumnShards: ['72075186224107692'], + HashSharding: { + Function: 'HASH_FUNCTION_CONSISTENCY_64', + Columns: ['id', 'author', 'title'], + }, + }, + StorageConfig: { + DataChannelCount: 64, + }, + }, + }, + }; + const result = [ + { + id: 1, + name: 'title', + keyColumnIndex: 1, + partitioningColumnIndex: 2, + type: 'Utf8', + notNull: true, + }, + { + id: 2, + name: 'author', + keyColumnIndex: 0, + partitioningColumnIndex: 1, + type: 'Utf8', + notNull: true, + }, + { + id: 3, + name: 'id', + keyColumnIndex: 2, + partitioningColumnIndex: 0, + type: 'Int64', + notNull: true, + }, + { + id: 4, + name: 'body', + keyColumnIndex: -1, + partitioningColumnIndex: -1, + type: 'Utf8', + notNull: false, + }, + ]; + expect(prepareSchemaData(EPathType.EPathTypeColumnTable, data)).toEqual(result); + }); + it('returns empty array if data is undefined, empty or null', () => { + expect(prepareSchemaData(EPathType.EPathTypeTable, {})).toEqual([]); + expect(prepareSchemaData(EPathType.EPathTypeTable, undefined)).toEqual([]); + expect(prepareSchemaData(EPathType.EPathTypeTable, null)).toEqual([]); + }); +}); + +describe('prepareViewSchema', () => { + it('correctly parses data', () => { + const data = [ + {name: 'cost', type: 'Int32'}, + {name: 'id', type: 'Int32'}, + {name: 'price', type: 'Double'}, + {name: 'value', type: 'Utf8?'}, + ]; + const result = [ + {type: 'Int32', name: 'cost'}, + {type: 'Int32', name: 'id'}, + {type: 'Double', name: 'price'}, + {type: 'Utf8', name: 'value'}, + ]; + + expect(prepareViewSchema(data)).toEqual(result); + }); + it('returns empty array if data is undefined or empty', () => { + expect(prepareViewSchema()).toEqual([]); + expect(prepareViewSchema([])).toEqual([]); + }); +}); diff --git a/src/containers/Tenant/Schema/SchemaViewer/__tests__/utils.test.ts b/src/containers/Tenant/Schema/SchemaViewer/__tests__/utils.test.ts new file mode 100644 index 000000000..74c6b6190 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/__tests__/utils.test.ts @@ -0,0 +1,103 @@ +import {getPartitioningKeys, getPrimaryKeys} from '../utils'; + +describe('getPartitioningKeys', () => { + it('returns column in the provided order', () => { + const data1 = [ + { + id: 1, + name: 'l_linenumber', + keyColumnIndex: 0, + partitioningColumnIndex: 1, + type: 'Int64', + notNull: true, + }, + { + id: 2, + name: 'l_orderkey', + keyColumnIndex: 1, + partitioningColumnIndex: 0, + type: 'Int64', + notNull: true, + }, + ]; + + expect(getPartitioningKeys(data1)).toEqual(['l_orderkey', 'l_linenumber']); + + const data2 = [ + { + id: 1, + name: 'title', + keyColumnIndex: 1, + partitioningColumnIndex: 2, + type: 'Utf8', + notNull: true, + }, + { + id: 2, + name: 'author', + keyColumnIndex: 0, + partitioningColumnIndex: 1, + type: 'Utf8', + notNull: true, + }, + { + id: 3, + name: 'id', + keyColumnIndex: 2, + partitioningColumnIndex: 0, + type: 'Int64', + notNull: true, + }, + { + id: 4, + name: 'body', + keyColumnIndex: -1, + partitioningColumnIndex: -1, + type: 'Utf8', + notNull: false, + }, + ]; + expect(getPartitioningKeys(data2)).toEqual(['id', 'author', 'title']); + }); +}); + +describe('getPrimaryKeys', () => { + it('returns column in the provided order', () => { + const data = [ + { + id: 1, + name: 'id', + keyColumnIndex: 2, + type: 'Uint64', + notNull: false, + autoIncrement: false, + }, + { + id: 2, + name: 'category_id', + keyColumnIndex: 0, + type: 'Uint64', + notNull: true, + autoIncrement: false, + }, + { + id: 3, + name: 'name', + keyColumnIndex: 1, + type: 'Utf8', + notNull: true, + autoIncrement: false, + }, + { + id: 4, + name: 'updated_on', + keyColumnIndex: -1, + type: 'Datetime', + notNull: false, + autoIncrement: false, + }, + ]; + + expect(getPrimaryKeys(data)).toEqual(['category_id', 'name', 'id']); + }); +}); diff --git a/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts b/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts index 7f6ee3606..8b1b34391 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts +++ b/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts @@ -103,15 +103,14 @@ function prepareColumnTableSchema(data: TColumnTableDescription = {}): SchemaDat const keyColumnIndex = KeyColumnNames?.findIndex((keyColumnName) => keyColumnName === Name) ?? -1; - const isPartitioningKeyColumn = Boolean( - HashColumns?.find((hashColumnName) => hashColumnName === Name), - ); + const partitioningColumnIndex = + HashColumns?.findIndex((hashColumnName) => hashColumnName === Name) ?? -1; return { id: Id, name: Name, keyColumnIndex, - isPartitioningKeyColumn, + partitioningColumnIndex, type: Type, notNull: NotNull, }; diff --git a/src/containers/Tenant/Schema/SchemaViewer/types.ts b/src/containers/Tenant/Schema/SchemaViewer/types.ts index 9a77d1959..6aea5ddd0 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/types.ts +++ b/src/containers/Tenant/Schema/SchemaViewer/types.ts @@ -4,7 +4,7 @@ export type SchemaData = { id?: number; name?: string; keyColumnIndex?: number; - isPartitioningKeyColumn?: boolean; + partitioningColumnIndex?: number; type?: string; notNull?: boolean; autoIncrement?: boolean; diff --git a/src/containers/Tenant/Schema/SchemaViewer/utils.ts b/src/containers/Tenant/Schema/SchemaViewer/utils.ts index 87ad817d8..7337a75cc 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/utils.ts +++ b/src/containers/Tenant/Schema/SchemaViewer/utils.ts @@ -1,14 +1,27 @@ +import type {WithRequiredFields} from '../../../../types/common'; + import type {SchemaData} from './types'; export function getPartitioningKeys(tableData: SchemaData[]): string[] { return tableData - .filter((row) => row.isPartitioningKeyColumn && row.name) - .map((row) => row.name!); + .filter((row): row is WithRequiredFields => + Boolean( + row.partitioningColumnIndex !== undefined && + row.partitioningColumnIndex !== -1 && + row.name, + ), + ) + .sort( + (column1, column2) => column1.partitioningColumnIndex - column2.partitioningColumnIndex, + ) + .map((row) => row.name); } export function getPrimaryKeys(tableData: SchemaData[]): string[] { return tableData - .filter((row) => row.keyColumnIndex !== undefined && row.keyColumnIndex !== -1 && row.name) - .sort((column1, column2) => column1.keyColumnIndex! - column2.keyColumnIndex!) - .map((row) => row.name!); + .filter((row): row is WithRequiredFields => + Boolean(row.keyColumnIndex !== undefined && row.keyColumnIndex !== -1 && row.name), + ) + .sort((column1, column2) => column1.keyColumnIndex - column2.keyColumnIndex) + .map((row) => row.name); } diff --git a/src/types/api/schema/columnEntity.ts b/src/types/api/schema/columnEntity.ts index 4657aca29..55e7f3591 100644 --- a/src/types/api/schema/columnEntity.ts +++ b/src/types/api/schema/columnEntity.ts @@ -1,4 +1,5 @@ import type {EColumnCodec, EUnit, TPathID, TStorageSettings, TTypeInfo} from './shared'; +import type {TFamilyDescription} from './table'; export interface TColumnTableDescription { Name?: string; @@ -75,15 +76,14 @@ interface TTtl { interface TColumnTableSchema { Columns?: TOlapColumnDescription[]; KeyColumnNames?: string[]; - KeyColumnIds?: number[]; - Engine?: EColumnTableEngine; - NextColumnId?: number; /** uint64 */ Version?: string; DefaultCompression?: TCompressionOptions; - EnableTiering?: boolean; + Indexes?: unknown; + Options?: TColumnTableSchemeOptions; + ColumnFamilies?: TFamilyDescription[]; } interface TColumnTableSchemaPreset { @@ -92,27 +92,34 @@ interface TColumnTableSchemaPreset { Schema?: TColumnTableSchema; } +interface TColumnTableSchemeOptions { + SchemeNeedActualization?: boolean; // default = false + ScanReaderPolicyName?: string; +} + interface TOlapColumnDescription { Id?: number; Name?: string; + Type?: string; - NotNull?: boolean; TypeId?: number; TypeInfo?: TTypeInfo; + + NotNull?: boolean; + + DictionaryEncoding?: TDictionaryEncodingSettings; + Serializer?: unknown; + StorageId?: string; + DefaultValue?: unknown; + DataAccessorConstructor?: unknown; + ColumnFamilyId?: number; + ColumnFamilyName?: string; } interface TColumnTableSharding { - /** uint64 */ - Version?: string; - /** uint64 */ ColumnShards?: string[]; - /** uint64 */ - AdditionalColumnShards?: string[]; - - UniquePrimaryKey?: boolean; - RandomSharding?: {}; HashSharding?: THashSharding; } @@ -120,8 +127,8 @@ interface TColumnTableSharding { interface THashSharding { Function?: EHashFunction; Columns?: string[]; - UniqueShardKey?: boolean; ActiveShardsCount?: number; + ModuloPartsCount?: number; } interface TCompressionOptions { @@ -129,12 +136,11 @@ interface TCompressionOptions { CompressionLevel?: number; } -enum EHashFunction { - HASH_FUNCTION_MODULO_N = 'HASH_FUNCTION_MODULO_N', - HASH_FUNCTION_CLOUD_LOGS = 'HASH_FUNCTION_CLOUD_LOGS', +interface TDictionaryEncodingSettings { + Enabled?: boolean; } -enum EColumnTableEngine { - COLUMN_ENGINE_NONE = 'COLUMN_ENGINE_NONE', - COLUMN_ENGINE_REPLACING_TIMESERIES = 'COLUMN_ENGINE_REPLACING_TIMESERIES', -} +type EHashFunction = + | 'HASH_FUNCTION_MODULO_N' + | 'HASH_FUNCTION_CLOUD_LOGS' + | 'HASH_FUNCTION_CONSISTENCY_64'; diff --git a/src/types/common.ts b/src/types/common.ts index ab885d759..3942b019e 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -1,2 +1,3 @@ export type ValueOf = T[keyof T]; export type ExtractType = T extends {type: infer U} ? U : string; +export type WithRequiredFields = Exclude & Required>;