Skip to content

GH-825: Add UUID canonical extension type - #903

Merged
jbonofre merged 5 commits into
apache:mainfrom
jhrotko:GH-825
Nov 28, 2025
Merged

GH-825: Add UUID canonical extension type#903
jbonofre merged 5 commits into
apache:mainfrom
jhrotko:GH-825

Conversation

@jhrotko

@jhrotkojhrotko commented Nov 3, 2025

Copy link
Copy Markdown
Contributor

What's Changed

Add Uuid Extension type canonical implementation in Java. The proposed implementation was validated in Dremio engine

Closes#825

@github-actions

This comment has been minimized.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous PR. It is not sufficient to move the existing files, the "uuid" type used in tests was never meant to implement the canonical type. #824

@jhrotkojhrotko changed the title GH-825: Move UuidType to arrow.vector from testGH-825: Implement Uuid canonical typeNov 7, 2025
@jhrotkojhrotko changed the title GH-825: Implement Uuid canonical typeGH-825: Add a canonical extension type UUIDNov 7, 2025
@jhrotko
jhrotko marked this pull request as ready for review November 7, 2025 13:58
@jhrotko

jhrotko commented Nov 7, 2025

Copy link
Copy Markdown
ContributorAuthor

@lidavidm could you take another look? Let me know if anything else is missing

@jhrotkojhrotko changed the title GH-825: Add a canonical extension type UUIDGH-825: Add UUID canonical extension typeNov 7, 2025

@bodduvbodduv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding the canonical type.

Comment threadvector/src/main/java/org/apache/arrow/vector/FixedSizeExtensionType.java Outdated
Comment threadvector/src/main/java/org/apache/arrow/vector/UuidVector.java Outdated
Comment threadvector/src/main/java/org/apache/arrow/vector/UuidVector.java Outdated
@lidavidmlidavidm added enhancement PRs that add or improve features. and removed breaking-change labels Nov 11, 2025
@github-actionsgithub-actionsBot added this to the 18.4.0 milestone Nov 11, 2025
@jhrotko

Copy link
Copy Markdown
ContributorAuthor

there are some integration tests failling:

 ################# FAILURES #################
FAILED TEST: custom_metadata Java producing, Go consuming
<class 'RuntimeError'>: Command failed: /root/go/bin/arrow-json-integration-test -arrow /tmp/tmprky9nvsk/020d3b5c_generated_custom_metadata.json_as_file -json /tmp/arrow-integration-ypsmstn1/generated_custom_metadata.json -mode VALIDATE
With output:
--------------
arrow-json: JSON schema:
schema:
fields: 4
- sort_of_pandas: type=int8, nullable
metadata: ["pandas": "{}"]
- lots_of_meta: type=int8, nullable
metadata: ["a": "{}", "b": "{}", "c": "{}", "d": "{}", "..": "{}", "w": "{}", "x": "{}", "y": "{}", "z": "{}"]
- unregistered_extension: type=int8, nullable
metadata: ["ARROW:extension:name": "!nonexistent", "ARROW:extension:metadata": "", "ARROW:integration:allow_unregistered_extension": "true"]
- list_with_odd_values: type=list<item: int32, nullable>, nullable
metadata: ["schema_custom_0": "{}", "schema_custom_1": "{}"]
Arrow schema:
schema:
fields: 4
- sort_of_pandas: type=int8, nullable
metadata: ["pandas": "{}"]
- lots_of_meta: type=int8, nullable
metadata: ["..": "{}", "a": "{}", "b": "{}", "c": "{}", "d": "{}", "w": "{}", "x": "{}", "y": "{}", "z": "{}"]
- unregistered_extension: type=int8, nullable
metadata: ["ARROW:extension:name": "!nonexistent", "ARROW:extension:metadata": "", "ARROW:integration:allow_unregistered_extension": "true"]
- list_with_odd_values: type=list<item: int32, nullable>, nullable
metadata: ["schema_custom_1": "{}", "schema_custom_0": "{}"]
arrow-json: schemas did not match
--------------
FAILED TEST: flight_sql Java producing, Go consuming
<class 'RuntimeError'>: Command failed: /root/go/bin/arrow-flight-integration-client -host localhost -port=43857 -scenario flight_sql
With output:
--------------
gc 1 @3.115s 0%: 0.078+0.59+0.008 ms clock, 0.31+0.37/0.34/0.60+0.032 ms cpu, 3->3->0 MB, 4 MB goal, 0 MB stacks, 0 MB globals, 4 P
panic: expected: schema:
fields: 1
- id: type=int64, nullable
metadata: ["ARROW:FLIGHT:SQL:TABLE_NAME": "test", "ARROW:FLIGHT:SQL:IS_AUTO_INCREMENT": "1", "ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE": "0", "ARROW:FLIGHT:SQL:TYPE_NAME": "type_test", "ARROW:FLIGHT:SQL:SCHEMA_NAME": "schema_test", "ARROW:FLIGHT:SQL:IS_SEARCHABLE": "1", "ARROW:FLIGHT:SQL:CATALOG_NAME": "catalog_test", "ARROW:FLIGHT:SQL:PRECISION": "100", "ARROW:FLIGHT:SQL:REMARKS": "test column"], got: schema:
fields: 1
- id: type=int64, nullable
metadata: ["ARROW:FLIGHT:SQL:PRECISION": "100", "ARROW:FLIGHT:SQL:REMARKS": "test column", "ARROW:FLIGHT:SQL:IS_AUTO_INCREMENT": "1", "ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE": "0", "ARROW:FLIGHT:SQL:SCHEMA_NAME": "schema_test", "ARROW:FLIGHT:SQL:TABLE_NAME": "test", "ARROW:FLIGHT:SQL:IS_SEARCHABLE": "1", "ARROW:FLIGHT:SQL:TYPE_NAME": "type_test", "ARROW:FLIGHT:SQL:CATALOG_NAME": "catalog_test"]
goroutine 1 [running]:
main.main()
/arrow/go/arrow/internal/flight_integration/cmd/arrow-flight-integration-client/main.go:52 +0x277
--------------
FAILED TEST: flight_sql:extension Java producing, Go consuming
<class 'RuntimeError'>: Command failed: /root/go/bin/arrow-flight-integration-client -host localhost -port=35551 -scenario flight_sql:extension
With output:
--------------
panic: expected: schema:
fields: 1
- id: type=int64, nullable
metadata: ["ARROW:FLIGHT:SQL:TABLE_NAME": "test", "ARROW:FLIGHT:SQL:IS_AUTO_INCREMENT": "1", "ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE": "0", "ARROW:FLIGHT:SQL:TYPE_NAME": "type_test", "ARROW:FLIGHT:SQL:SCHEMA_NAME": "schema_test", "ARROW:FLIGHT:SQL:IS_SEARCHABLE": "1", "ARROW:FLIGHT:SQL:CATALOG_NAME": "catalog_test", "ARROW:FLIGHT:SQL:PRECISION": "100", "ARROW:FLIGHT:SQL:REMARKS": "test column"], got: schema:
fields: 1
- id: type=int64, nullable
metadata: ["ARROW:FLIGHT:SQL:PRECISION": "100", "ARROW:FLIGHT:SQL:REMARKS": "test column", "ARROW:FLIGHT:SQL:IS_AUTO_INCREMENT": "1", "ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE": "0", "ARROW:FLIGHT:SQL:SCHEMA_NAME": "schema_test", "ARROW:FLIGHT:SQL:TABLE_NAME": "test", "ARROW:FLIGHT:SQL:IS_SEARCHABLE": "1", "ARROW:FLIGHT:SQL:TYPE_NAME": "type_test", "ARROW:FLIGHT:SQL:CATALOG_NAME": "catalog_test"]
goroutine 1 [running]:
main.main()
/arrow/go/arrow/internal/flight_integration/cmd/arrow-flight-integration-client/main.go:52 +0x277
--------------
FAILED TEST: flight_sql Go producing, Java consuming
<class 'RuntimeError'>: Command failed: java -Dio.netty.tryReflectionSetAccessible=true -Darrow.struct.conflict.policy=CONFLICT_APPEND --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED -XX:-UsePerfData --add-reads=org.apache.arrow.flight.core=ALL-UNNAMED -cp /build/java/flight/flight-integration-tests/target/flight-integration-tests-19.0.0-SNAPSHOT-jar-with-dependencies.jar org.apache.arrow.flight.integration.tests.IntegrationTestClient -port 40707 -scenario flight_sql
With output:
--------------
WARNING: Unknown module: org.apache.arrow.flight.core specified to --add-reads
WARNING: Unknown module: org.apache.arrow.memory.core specified to --add-opens
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
Unknown error
expected: '%!s(bool=true)', got: '%!s(bool=false)'
--------------
FAILED TEST: flight_sql:extension Go producing, Java consuming
<class 'RuntimeError'>: Command failed: java -Dio.netty.tryReflectionSetAccessible=true -Darrow.struct.conflict.policy=CONFLICT_APPEND --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED -XX:-UsePerfData --add-reads=org.apache.arrow.flight.core=ALL-UNNAMED -cp /build/java/flight/flight-integration-tests/target/flight-integration-tests-19.0.0-SNAPSHOT-jar-with-dependencies.jar org.apache.arrow.flight.integration.tests.IntegrationTestClient -port 32823 -scenario flight_sql:extension
With output:
--------------
WARNING: Unknown module: org.apache.arrow.flight.core specified to --add-reads
WARNING: Unknown module: org.apache.arrow.memory.core specified to --add-opens
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
Unknown error
expected: '%!s(bool=true)', got: '%!s(bool=false)'
--------------
FAILED TEST: custom_metadata Java producing, Go consuming
<class 'RuntimeError'>: Go C Data Integration call failed: Schemas are different:
- Json Schema: schema:
fields: 4
- sort_of_pandas: type=int8, nullable
metadata: ["pandas": "{}"]
- lots_of_meta: type=int8, nullable
metadata: ["a": "{}", "b": "{}", "c": "{}", "d": "{}", "..": "{}", "w": "{}", "x": "{}", "y": "{}", "z": "{}"]
- unregistered_extension: type=int8, nullable
metadata: ["ARROW:extension:name": "!nonexistent", "ARROW:extension:metadata": "", "ARROW:integration:allow_unregistered_extension": "true"]
- list_with_odd_values: type=list<item: int32, nullable>, nullable
metadata: ["schema_custom_0": "{}", "schema_custom_1": "{}"]
- Imported Schema: schema:
fields: 4
- sort_of_pandas: type=int8, nullable
metadata: ["pandas": "{}"]
- lots_of_meta: type=int8, nullable
metadata: ["..": "{}", "a": "{}", "b": "{}", "c": "{}", "d": "{}", "w": "{}", "x": "{}", "y": "{}", "z": "{}"]
- unregistered_extension: type=int8, nullable
metadata: ["ARROW:extension:name": "!nonexistent", "ARROW:extension:metadata": "", "ARROW:integration:allow_unregistered_extension": "true"]
- list_with_odd_values: type=list<item: int32, nullable>, nullable
metadata: ["schema_custom_1": "{}", "schema_custom_0": "{}"]

It seems the metadata elements when it's a map the order is not maintained. I this a known issue? @lidavidm

@lidavidm

Copy link
Copy Markdown
Member

It's apache/arrow-go#571

@jhrotko

Copy link
Copy Markdown
ContributorAuthor

@lidavidm it was merged, should this be re-run?

@jhrotko

Copy link
Copy Markdown
ContributorAuthor

@lidavidm anything else missing?

@bodduvbodduv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for addressing the comments.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has conflicts

Comment on lines +67 to +80
/** Registers the UuidType in the extension type registry. */
private static final AtomicBoolean registered = new AtomicBoolean(false);

static {
ExtensionTypeRegistry.register(INSTANCE);
}

/** Register the extension type so it can be used globally. */
public static void ensureRegistered() {
if (!registered.getAndSet(true)) {
// The values don't matter, we just need an instance
ExtensionTypeRegistry.register(UuidType.INSTANCE);
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't all of this redundant? If we have the static initializer, then ensureRegistered can just be a no-op. Or if we're going to use the atomic boolean, then we don't need the static initializer.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is only used in tests similarly to Opaque extension type

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no static initializer there. There is one here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I added the static implementation following your suggestion

@jbonofre

Copy link
Copy Markdown
Member

Can you rebase please ?

@lidavidm

Copy link
Copy Markdown
Member

I think these failures are related to upstream and won't go away on rebase.

@jbonofre

Copy link
Copy Markdown
Member

I will check as I try to stabilize our CI. It fails way too often and it's an issue in PRs.

@jbonofre

Copy link
Copy Markdown
Member

The PR looks good to me. I will merge as soon as CI is green.

@jbonofre

Copy link
Copy Markdown
Member

CI failures are not relevant to this PR. I'm going ahead and merging.

@jbonofre
jbonofre merged commit 82baf18 into apache:mainNov 28, 2025
22 of 25 checks passed
@jhrotko
jhrotko deleted the GH-825 branch November 28, 2025 12:30
jbonofre pushed a commit that referenced this pull request Jan 19, 2026
## What's Changed
The current UUID vector implementation creates new buffer slices when
reading values through holders, which has several drawbacks:
- Memory overhead: Each slice creates a new ArrowBuf object
- Performance impact: Buffer slicing is slower than direct buffer
indexing
- Inconsistency: Other fixed-width types (like Decimal) use buffer
indexing with a `start` offset field
### Proposed Changes
1. Add `start` field to UUID holders to track buffer offsets:
- `UuidHolder`: Add `public int start = 0;`
- `NullableUuidHolder`: Add `public int start = 0;`
2. Update `UuidVector` to use buffer indexing
3. Update readers and writers
### Related Work
- Original UUID extension type implementation: GH-825 (#903)
Closes#948
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementPRs that add or improve features.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce type support for UUID as a canonical extension type

4 participants

@jhrotko@lidavidm@jbonofre@bodduv