You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hive: DECIMAL(p,s) nested in ARRAY<STRUCT<...>> / MAP<...> fails column ingestion — table lands with zero columns while the workflow reports 100% success #30061
A Hive column whose type nests a DECIMAL(p,s) inside a complex type — ARRAY<STRUCT<... DECIMAL(p,s) ...>> or MAP<..., DECIMAL(p,s)> — makes get_columns() raise:
ValueError: invalid literal for int() with base 10: '16,4'
The exception is caught in sql_column_handler.py:297 and only logged as a WARNING (the traceback is visible only at loggerLevel: DEBUG). As a result:
the ingestion workflow finishes with Workflow Success %: 100.0, Errors: 0, Warnings: 0,
but the table is created in OpenMetadata with an empty column list.
The failure is therefore silent: the pipeline reports success, and nothing in its status indicates that all column metadata for that table was dropped. We only noticed because a table showed up in the UI with no columns.
A top-levelSTRUCT<... DECIMAL(p,s) ...> works fine — this is the case that was fixed in #13429 by adding an attype.startswith("struct") branch to hive/utils.py. That fix, however, only covers struct: when the same DECIMAL sits inside an ARRAY or a MAP, the outer type is no longer struct and the same ValueError comes back. So this looks like an incomplete fix of #13429 rather than a new defect.
Where it happens — ingestion/src/metadata/ingestion/source/database/hive/utils.py, get_columns():
col_raw_type=col_typeattype=re.sub(r"\(.*\)", "", col_type) # greedy: eats everything between the FIRST "(" and the LAST ")"col_type=re.search(r"^\w+", col_type).group(0) # outer type name -> "array"
...
charlen=re.search(r"\(([\d,]+)\)", col_raw_type.lower()) # matches the NESTED decimal's params -> "16,4"ifcharlen:
charlen=charlen.group(1)
ifattype=="decimal":
prec, scale=charlen.split(",")
args= (int(prec), int(scale))
elifattype.startswith("struct"): # only STRUCT is special-cased (added in #13429)args= []
else:
args= (int(charlen),) # int("16,4") -> ValueErrorcoltype=coltype(*args)
Two things combine:
charlen is searched over the whole raw type string, so for a complex type it picks up the parameters of a nested type (decimal(16,4) → "16,4").
Whether the type is complex is decided from attype, which the greedy re.sub has already mangled: for array<struct<fee_a:decimal(16,4),...>> it becomes "array<struct<fee_a:decimal,...", so attype.startswith("struct") is False and execution falls through to int(charlen).
To Reproduce
Create the tables below in Hive (the last three are controls that ingest fine):
CREATEDATABASEIF NOT EXISTS test_nested_types;
-- FAILS: DECIMAL(p,s) nested inside ARRAY<STRUCT<...>>
CREATE EXTERNAL TABLE test_nested_types.array_struct_decimal(
id STRING,
items ARRAY<STRUCT<fee_a: DECIMAL(16,4), fee_b: DECIMAL(16,4), amount: BIGINT, item_name: STRING>>
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET;
-- FAILS: DECIMAL(p,s) nested inside MAP<...>
CREATE EXTERNAL TABLE test_nested_types.map_decimal(
id STRING,
fees MAP<STRING, DECIMAL(10,2)>
) STORED AS PARQUET;
-- OK (control): top-level STRUCT with a DECIMAL (the #13429 case)
CREATE EXTERNAL TABLE test_nested_types.struct_decimal(
id STRING,
fees STRUCT<fee_a: DECIMAL(16,4), amount: BIGINT>
) STORED AS PARQUET;
-- OK (control): complex type without a nested DECIMAL
CREATE EXTERNAL TABLE test_nested_types.array_struct_bigint(
id STRING,
items ARRAY<STRUCT<amount: BIGINT, item_name: STRING>>
) STORED AS PARQUET;
-- OK (control): top-level DECIMAL
CREATE EXTERNAL TABLE test_nested_types.plain_decimal(
id STRING,
fee DECIMAL(16,4),
name VARCHAR(255)
) STORED AS PARQUET;
The type string the metastore returns for the failing column is:
array_struct_decimal and map_decimal should ingest with their full column list, with the nested STRUCT fields as children — the same way struct_decimal and array_struct_bigint already do.
Separately: even if column reflection does fail, the pipeline should not report Errors: 0 / Success 100% while silently dropping every column of a table. A table ingested with zero columns is indistinguishable from a healthy run in the pipeline status.
Minor, possibly worth a separate issue: ColumnTypeParser._parse_primitive_datatype_string() does not set precision / scale for nested decimals — it puts the precision into dataLength instead (dataLength=16, precision=None, scale=None for a decimal(16,4) struct field). Top-level decimal columns are handled correctly by check_col_precision().
I have a working fix and a self-contained Docker repro; happy to open a PR if that helps.
Pre-submission checklist
I searched for duplicate issues.
I removed credentials, hostnames, emails, and other sensitive data from logs and config.
Connector
Hive
Feature area
Metadata ingestion
Describe the bug
A Hive column whose type nests a
DECIMAL(p,s)inside a complex type —ARRAY<STRUCT<... DECIMAL(p,s) ...>>orMAP<..., DECIMAL(p,s)>— makesget_columns()raise:The exception is caught in
sql_column_handler.py:297and only logged as aWARNING(the traceback is visible only atloggerLevel: DEBUG). As a result:Workflow Success %: 100.0,Errors: 0,Warnings: 0,The failure is therefore silent: the pipeline reports success, and nothing in its status indicates that all column metadata for that table was dropped. We only noticed because a table showed up in the UI with no columns.
A top-level
STRUCT<... DECIMAL(p,s) ...>works fine — this is the case that was fixed in #13429 by adding anattype.startswith("struct")branch tohive/utils.py. That fix, however, only coversstruct: when the sameDECIMALsits inside anARRAYor aMAP, the outer type is no longerstructand the sameValueErrorcomes back. So this looks like an incomplete fix of #13429 rather than a new defect.Where it happens —
ingestion/src/metadata/ingestion/source/database/hive/utils.py,get_columns():Two things combine:
charlenis searched over the whole raw type string, so for a complex type it picks up the parameters of a nested type (decimal(16,4)→"16,4").attype, which the greedyre.subhas already mangled: forarray<struct<fee_a:decimal(16,4),...>>it becomes"array<struct<fee_a:decimal,...", soattype.startswith("struct")isFalseand execution falls through toint(charlen).To Reproduce
The type string the metastore returns for the failing column is:
DatabaseMetadataingestion against that schema (config below).Result per table:
array_struct_decimalarray<struct<...decimal(16,4)...>>map_decimalmap<string,decimal(10,2)>struct_decimalstruct<...decimal(16,4)...>array_struct_bigintarray<struct<...bigint...>>plain_decimaldecimal(16,4)Expected behavior
array_struct_decimalandmap_decimalshould ingest with their full column list, with the nestedSTRUCTfields as children — the same waystruct_decimalandarray_struct_bigintalready do.Separately: even if column reflection does fail, the pipeline should not report
Errors: 0/Success 100%while silently dropping every column of a table. A table ingested with zero columns is indistinguishable from a healthy run in the pipeline status.Connection / ingestion config
Logs
[2026-07-14 21:13:13] DEBUG {metadata.Ingestion:sql_column_handler:296} - Traceback (most recent call last): File ".../site-packages/metadata/ingestion/source/database/sql_column_handler.py", line 292, in get_columns_and_constraints columns = self._get_columns_internal( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File ".../site-packages/metadata/ingestion/source/database/sql_column_handler.py", line 238, in _get_columns_internal return inspector.get_columns( ^^^^^^^^^^^^^^^^^^^^^^ File ".../site-packages/sqlalchemy/engine/reflection.py", line 497, in get_columns col_defs = self.dialect.get_columns( ^^^^^^^^^^^^^^^^^^^^^^^^^ File ".../site-packages/metadata/ingestion/source/database/hive/utils.py", line 73, in get_columns args = (int(charlen),) ^^^^^^^^^^^^ ValueError: invalid literal for int() with base 10: '16,4' [2026-07-14 21:13:13] WARNING {metadata.Ingestion:sql_column_handler:297} - Unexpected exception getting columns for table [array_struct_decimal] (schema: 'test_nested_types', db: 'default'): invalid literal for int() with base 10: '16,4' # ... and the very same run finishes with: Processed records: 10 Updated records: 0 Warnings: 0 Errors: 0 Success %: 100.0 Workflow Success %: 100.0The identical trace is raised for
map_decimalwith'10,2'.OS
macOS 14.4 (Docker); also seen on Linux
Python version
3.12
OpenMetadata version
1.12.5
OpenMetadata Ingestion package version
openmetadata-ingestion==1.12.12
Additional context
mainand in the1.13branch, so this should reproduce there as well.STRUCT. Thearray/mapcases were not covered, and they still hit the same line.ColumnTypeParser._parse_primitive_datatype_string()does not setprecision/scalefor nested decimals — it puts the precision intodataLengthinstead (dataLength=16, precision=None, scale=Nonefor adecimal(16,4)struct field). Top-level decimal columns are handled correctly bycheck_col_precision().I have a working fix and a self-contained Docker repro; happy to open a PR if that helps.
Pre-submission checklist