Skip to content

Introduce AttributeMap interface#5139

Open
adutra wants to merge 6 commits into
apache:mainfrom
adutra:attribute-map-1
Open

Introduce AttributeMap interface#5139
adutra wants to merge 6 commits into
apache:mainfrom
adutra:attribute-map-1

Conversation

@adutra

@adutra adutra commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

This change introduces AttributeMap, a map-like interface with type-safe key-value pairs.

This change only introduces the new interface, but it is currently unused.

Future work will adapt the following areas to the new AttributeMap interface:

  • Principal attributes
  • Event attributes

Checklist

  • 🛡️ Don't disclose security issues! (contact security@apache.org)
  • 🔗 Clearly explained why the changes are needed, or linked related issues: Fixes #
  • 🧪 Added/updated tests with good coverage, or manually tested (and explained how)
  • 💡 Added comments for complex logic
  • 🧾 Updated CHANGELOG.md (if needed)
  • 📚 Updated documentation in site/content/in-dev/unreleased (if needed)

This change introduces `AttributeMap`, a map-like interface with type-safe key-value pairs.

This change only introduces the new interface, but it is currently unused.

Future work will adapt the following areas to the new `AttributeMap` interface:

- Principal attributes
- Event attributes
Copilot AI review requested due to automatic review settings July 24, 2026 12:23
@github-project-automation github-project-automation Bot moved this to PRs In Progress in Basic Kanban Board Jul 24, 2026
@adutra

adutra commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

This is a follow-up work discussed here:

#5085 (comment)

Copilot AI 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.

Pull request overview

This PR introduces a new typed, map-like AttributeMap API in polaris-core (plus mutable/immutable implementations and tests), intended to support future migrations of principal and event attributes away from raw Map<String, Object>.

Changes:

  • Added AttributeMap sealed interface with typed AttributeKey<T> / Attribute<T> and core read/write operations.
  • Added AbstractAttributeMap (HashMap-backed) plus MutableAttributeMap and ImmutableAttributeMap implementations (with builders).
  • Added shared and implementation-specific JUnit tests for read paths, builder behavior, and (im)mutability.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
polaris-core/src/main/java/org/apache/polaris/core/collection/AttributeMap.java New public API for typed attribute maps, including keys/attributes and core operations.
polaris-core/src/main/java/org/apache/polaris/core/collection/AbstractAttributeMap.java Base HashMap-backed implementation and shared builder logic.
polaris-core/src/main/java/org/apache/polaris/core/collection/MutableAttributeMap.java Mutable implementation and builder.
polaris-core/src/main/java/org/apache/polaris/core/collection/ImmutableAttributeMap.java Immutable implementation with unmodifiable views and mutation guards.
polaris-core/src/test/java/org/apache/polaris/core/collection/AbstractAttributeMapTest.java Shared behavior tests for both implementations.
polaris-core/src/test/java/org/apache/polaris/core/collection/MutableAttributeMapTest.java Mutable-specific mutation/view tests.
polaris-core/src/test/java/org/apache/polaris/core/collection/ImmutableAttributeMapTest.java Immutable-specific immutability tests.

@adutra
adutra force-pushed the attribute-map-1 branch from ca2b5b8 to 5f4f1ea Compare July 24, 2026 15:14
@adutra
adutra force-pushed the attribute-map-1 branch from 5f4f1ea to f9c7185 Compare July 24, 2026 16:00
public abstract M build();
}

private final Map<AttributeKey<?>, @Nullable Object> delegate = new HashMap<>();

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.

nit: why delegate? This class constructs and owns this piece of data 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I named it delegate since this class is also a map, but delegates actual storage to another map.

protected AbstractAttributeMap() {}

protected AbstractAttributeMap(AttributeMap toCopy) {
toCopy.entrySet().forEach(entry -> delegate.put(entry.key(), entry.value()));

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.

nit: we could probably add from() to the Builder and make constructors just take the map from the builder without re-copying... WDYT?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The builders already have a "from" method, but it's called putAll currently.
As for changing the copy constructors: I prefer to keep the defensive copy in place, that's safer in the general case imho (the constructors are public).

@SuppressWarnings("unchecked")
@Nullable
private static <T> T cast(@Nullable Object o) {
return (T) o;

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.

Could we require AttributeKey to contain a Class<T> and then use Class.cast()?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I played with the idea. But it won't buy us much. Only non-generic attribute keys could expose a Class<T> property, others (e.g. List<String>) simply cannot expose such a property. Even using the "type token" idiom wouldn't make that any better: you would be able to expose the attribute's type as a ParameterizedType property, but you still wouldn't be able to use that property to perform a "safe" cast (there is no ParameterizedType.cast() method).

That's why the idea here is to move the invariants to the API: in AttributeMap, there is no way for a writer to add an attribute with a wrong type and make a reader fail with a ClassCastException. It is therefore OK to just do an unsafe cast when reading the attribute's value back.

(well, in fact there is one way to break it: if a writer purposely adds two attributes with the same string key and different types, that would break readers – but that should be seen as an API abuse/misuse.)

@github-project-automation github-project-automation Bot moved this from PRs In Progress to Ready to merge in Basic Kanban Board Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants