This is an automated email from the ASF dual-hosted git repository. liuxun pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push: new 85e9918ac [#5454] feat(web): different data table column types are supported depending on the provider (#5636) 85e9918ac is described below commit 85e9918ac0058cc58fad87bc63716cb977c244e3 Author: JUN <oren....@gmail.com> AuthorDate: Thu Nov 21 17:18:44 2024 +0800 [#5454] feat(web): different data table column types are supported depending on the provider (#5636) ### What changes are proposed in this pull request? When creating or updating a table, the column types will vary depending on the provider. ### Why are these changes needed? Fixes: #5454 ### Does this PR introduce _any_ user-facing changes? No. ### How was this patch tested? Tested by creating and updating tables with different providers to verify that the column types differ as expected and match the [documentation](https://gravitino.apache.org/docs/0.6.1-incubating/lakehouse-iceberg-catalog/#table-column-types). Additionally, verified the success of the create/update operations. --- .../metalake/rightContent/CreateTableDialog.js | 57 +++--- web/web/src/lib/utils/initial.js | 196 ++++++++++++++++----- 2 files changed, 181 insertions(+), 72 deletions(-) diff --git a/web/web/src/app/metalakes/metalake/rightContent/CreateTableDialog.js b/web/web/src/app/metalakes/metalake/rightContent/CreateTableDialog.js index df0091a26..547382dcd 100644 --- a/web/web/src/app/metalakes/metalake/rightContent/CreateTableDialog.js +++ b/web/web/src/app/metalakes/metalake/rightContent/CreateTableDialog.js @@ -72,7 +72,7 @@ import { import Icon from '@/components/Icon' // Import Redux hooks and actions -import { useAppDispatch } from '@/lib/hooks/useStore' +import { useAppDispatch, useAppSelector } from '@/lib/hooks/useStore' import { createTable, updateTable } from '@/lib/store/metalakes' // Import form validation libraries @@ -85,7 +85,7 @@ import { groupBy } from 'lodash-es' import { genUpdates } from '@/lib/utils' import { nameRegex, nameRegexDesc, keyRegex } from '@/lib/utils/regex' import { useSearchParams } from 'next/navigation' -import { tableColumnTypes } from '@/lib/utils/initial' +import { getRelationalColumnTypeMap, getParameterizedColumnType } from '@/lib/utils/initial' // Default form values const defaultFormValues = { @@ -138,6 +138,10 @@ const CreateTableDialog = props => { const catalogType = searchParams.get('type') const schemaName = searchParams.get('schema') + const store = useAppSelector(state => state.metalakes) + const currentCatalog = store.catalogs.find(ca => ca.name === catalog) + const columnTypes = getRelationalColumnTypeMap(currentCatalog?.provider) + // Component state const [innerProps, setInnerProps] = useState([]) const [tableColumns, setTableColumns] = useState([{ name: '', type: '', nullable: true, comment: '' }]) @@ -210,7 +214,8 @@ const CreateTableDialog = props => { if (field === 'type') { updatedColumns[index].typeSuffix = '' updatedColumns[index].paramErrors = '' - if (tableColumnTypes.find(type => type.key === value)?.params) { + + if (getParameterizedColumnType(value)) { updatedColumns[index].paramValues = [] } } @@ -222,7 +227,7 @@ const CreateTableDialog = props => { const transformParamValues = index => { let updatedColumns = [...tableColumns] - const validateParams = tableColumnTypes.find(type => type.key === updatedColumns[index].type)?.validateParams + const validateParams = getParameterizedColumnType(updatedColumns[index].type)?.validateParams const paramValues = updatedColumns[index].paramValues.filter(param => param !== undefined).map(Number) const validateResult = validateParams(paramValues) @@ -592,9 +597,9 @@ const CreateTableDialog = props => { data-refer={`column-type-${index}`} renderValue={selected => <Box>{`${selected}${column.typeSuffix || ''}`}</Box>} > - {tableColumnTypes.map(type => ( - <MenuItem key={type.key} value={type.key}> - {type.key} + {columnTypes.map(type => ( + <MenuItem key={type} value={type}> + {type} </MenuItem> ))} </Select> @@ -621,28 +626,26 @@ const CreateTableDialog = props => { }) } - return tableColumnTypes - .find(t => t.key === column.type) - ?.params?.map((param, paramIndex) => ( - <TextField - key={paramIndex} - size='small' - type='number' - sx={{ minWidth: 60 }} - value={column.paramValues?.[paramIndex] || ''} - onChange={e => { - const newParamValues = [...(column.paramValues || [])] - newParamValues[paramIndex] = e.target.value - handleColumnChange({ index, field: 'paramValues', value: newParamValues }) - }} - placeholder={`${param}`} - data-refer={`column-param-${index}-${paramIndex}`} - inputProps={{ min: 0 }} - /> - )) + return getParameterizedColumnType(column.type)?.params?.map((param, paramIndex) => ( + <TextField + key={paramIndex} + size='small' + type='number' + sx={{ minWidth: 60 }} + value={column.paramValues?.[paramIndex] || ''} + onChange={e => { + const newParamValues = [...(column.paramValues || [])] + newParamValues[paramIndex] = e.target.value + handleColumnChange({ index, field: 'paramValues', value: newParamValues }) + }} + placeholder={`${param}`} + data-refer={`column-param-${index}-${paramIndex}`} + inputProps={{ min: 0 }} + /> + )) })()} {selectedColumnIndex !== index && - tableColumnTypes.find(type => type.key === column.type)?.params && + getParameterizedColumnType(column.type)?.params && column.paramValues && transformParamValues(index)} </Box> diff --git a/web/web/src/lib/utils/initial.js b/web/web/src/lib/utils/initial.js index 1ca2854da..6c599f9ee 100644 --- a/web/web/src/lib/utils/initial.js +++ b/web/web/src/lib/utils/initial.js @@ -347,37 +347,23 @@ export const providers = [ } ] -export const tableColumnTypes = [ - { key: 'boolean' }, - { key: 'byte' }, - { key: 'short' }, - { key: 'integer' }, - { key: 'long' }, - { key: 'float' }, - { key: 'double' }, - { - key: 'decimal', - params: ['precision', 'scale'], +const parameterizedColumnTypes = { + char: { + params: ['length'], validateParams: params => { - if (params.length !== 2) { + if (params.length !== 1) { return { valid: false, - message: 'Please set precision and scale' + message: 'Please set length' } } - const [param1, param2] = params - if (param1 <= 0 || param1 > 38) { - return { - valid: false, - message: 'The precision must be between 1 and 38' - } - } + const length = params[0] - if (param2 < 0 || param2 > param1) { + if (length <= 0) { return { valid: false, - message: 'The scale must be between 0 and the precision' + message: 'The length must be greater than 0' } } @@ -386,28 +372,28 @@ export const tableColumnTypes = [ } } }, - { key: 'date' }, - { key: 'time' }, - { key: 'timestamp' }, - { key: 'timestamp_tz' }, - { key: 'string' }, - { - key: 'char', - params: ['length'], + decimal: { + params: ['precision', 'scale'], validateParams: params => { - if (params.length !== 1) { + if (params.length !== 2) { return { valid: false, - message: 'Please set length' + message: 'Please set precision and scale' } } - const length = params[0] + const [param1, param2] = params + if (param1 <= 0 || param1 > 38) { + return { + valid: false, + message: 'The precision must be between 1 and 38' + } + } - if (length <= 0) { + if (param2 < 0 || param2 > param1) { return { valid: false, - message: 'The length must be greater than 0' + message: 'The scale must be between 0 and the precision' } } @@ -416,8 +402,7 @@ export const tableColumnTypes = [ } } }, - { - key: 'varchar', + fixed: { params: ['length'], validateParams: params => { if (params.length !== 1) { @@ -441,10 +426,7 @@ export const tableColumnTypes = [ } } }, - { key: 'interval_day' }, - { key: 'interval_year' }, - { - key: 'fixed', + varchar: { params: ['length'], validateParams: params => { if (params.length !== 1) { @@ -467,7 +449,131 @@ export const tableColumnTypes = [ valid: true } } - }, - { key: 'uuid' }, - { key: 'binary' } -] + } +} + +export const getParameterizedColumnType = type => { + if (Object.keys(parameterizedColumnTypes).includes(type)) { + return parameterizedColumnTypes[type] + } +} + +const relationalColumnTypeMap = { + 'lakehouse-iceberg': [ + 'binary', + 'boolean', + 'date', + 'decimal', + 'double', + 'fixed', + 'float', + 'integer', + 'long', + 'string', + 'time', + 'timestamp', + 'timestamp_tz', + 'uuid' + ], + + hive: [ + 'binary', + 'boolean', + 'byte', + 'char', + 'date', + 'decimal', + 'double', + 'float', + 'integer', + 'interval_day', + 'interval_year', + 'long', + 'short', + 'string', + 'timestamp', + 'varchar' + ], + + 'jdbc-mysql': [ + 'binary', + 'byte', + 'byte unsigned', + 'char', + 'date', + 'decimal', + 'double', + 'float', + 'integer', + 'integer unsigned', + 'long', + 'long unsigned', + 'short', + 'short unsigned', + 'string', + 'time', + 'timestamp', + 'varchar' + ], + 'jdbc-postgresql': [ + 'binary', + 'boolean', + 'char', + 'date', + 'decimal', + 'double', + 'float', + 'integer', + 'long', + 'short', + 'string', + 'time', + 'timestamp', + 'timestamp_tz', + 'varchar' + ], + + 'jdbc-doris': [ + 'boolean', + 'byte', + 'char', + 'date', + 'decimal', + 'double', + 'float', + 'integer', + 'long', + 'short', + 'string', + 'timestamp', + 'varchar' + ], + + 'lakehouse-paimon': [ + 'binary', + 'boolean', + 'byte', + 'char', + 'date', + 'decimal', + 'double', + 'fixed', + 'float', + 'integer', + 'long', + 'short', + 'string', + 'time', + 'timestamp', + 'timestamp_tz', + 'varchar' + ] +} + +export const getRelationalColumnTypeMap = catalog => { + if (Object.keys(relationalColumnTypeMap).includes(catalog)) { + return relationalColumnTypeMap[catalog] + } + + return [] +}