Repository navigation
REST: Don't URL-encode view names in create_view and register_view bodies - #4151
Conversation
|
|
||
|
|
||
| @pytest.mark.integration | ||
| def test_rest_identifiers_with_special_characters( |
There was a problem hiding this comment.
integration test that checks special character handling on relevant endpoints
|
|
||
| request = CreateViewRequest( | ||
| name=namespace_and_view["view"], | ||
| name=self._identifier_to_validated_tuple(identifier)[-1], |
There was a problem hiding this comment.
use _identifier_to_validated_tuple for request bodies so names aren't escaped, and escape only in URL paths.
mirrors CreateTableRequest
iceberg-python/pyiceberg/catalog/rest/__init__.py
Lines 1230 to 1238 in 12d4a9a
There was a problem hiding this comment.
Idea (not necessarily for this PR): what if Identifier still acted like a tuple but had named methods on it as well? I really don't like the -1 here, since it's not always intuitive why we're doing that.
Could you add a comment explaining what you're trying to do?
| identifier_tuple = self._identifier_to_validated_tuple(identifier) | ||
| if self.table_exists(identifier): | ||
| raise TableAlreadyExistsError(f"Table {namespace}.{view} already exists") | ||
| raise TableAlreadyExistsError(f"Table {'.'.join(identifier_tuple)} already exists") | ||
|
|
||
| request = RegisterViewRequest(name=view, metadata_location=metadata_location) | ||
| request = RegisterViewRequest(name=identifier_tuple[-1], metadata_location=metadata_location) | ||
| serialized_json = request.model_dump_json().encode(UTF8) | ||
| response = self._session.post( | ||
| self.url(Endpoints.register_view, namespace=namespace), | ||
| self.url(Endpoints.register_view, namespace=namespace_and_view["namespace"]), |
There was a problem hiding this comment.
follows register_table
iceberg-python/pyiceberg/catalog/rest/__init__.py
Lines 1352 to 1360 in 12d4a9a
There was a problem hiding this comment.
The bug was using the wrong helper in the request body. The body should not be escaped, the URL should be. We should probably do some refactoring here (esp. with the naming).
| Helper | Escaped? | Returns for ("ns", "my view") |
Use for |
|---|---|---|---|
_identifier_to_validated_tuple(identifier) |
No | ("ns", "my view") |
JSON body, error messages. Raises NoSuchIdentifierError if there's no namespace |
_split_identifier_for_path(identifier, kind) |
Yes | {"namespace": "ns", "view": "my%20view"} |
URL path only |
There was a problem hiding this comment.
🟢 Approval recommended
The focused fix preserves path encoding, covers both affected request bodies with regression tests, and has no identified blocking issues.
No open issues 🎉
What changed in this PR
Fixes REST view creation and registration so names containing special characters remain unchanged in JSON request bodies, while URL paths retain encoding.
Changes:
- Uses raw view names in
create_viewandregister_viewpayloads. - Adds regression tests for both payloads and integration coverage for special-character identifiers.
| File | Description |
|---|---|
tests/integration/test_catalog.py |
Adds REST round-trip checks for namespaces, tables, and views with special characters. |
tests/catalog/test_rest.py |
Verifies create and register view requests preserve raw names in JSON bodies. |
pyiceberg/catalog/rest/__init__.py |
Separates raw view names from encoded paths and uses readable identifiers in conflict errors. |
Review effort: Balanced
| rest_catalog.create_view(view_identifier, view.schema(), view.current_version()) | ||
| assert rest_catalog.view_exists(view_identifier) | ||
| assert rest_catalog.list_views(namespace) == [view_identifier] | ||
| # TODO: Cover register_view once View exposes its metadata location (#4088) |
There was a problem hiding this comment.
we should add register_view here, but its not yet merged
There was a problem hiding this comment.
added register_view here after rebasing #4088
create_view and register_view took the view name from _split_identifier_for_path, which URL-quotes it for use as a path segment, and put it in the JSON request body. A view named "my view" was created as "my%20view". Use the raw name, as create_table and register_table already do. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Round-trip namespace, table, and view names containing spaces, reserved URL characters, and non-ASCII text through path, query, and body parameters. This catches encoding mistakes like the double-encoded list_namespaces parent and the encoded view name in create_view. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
commit_table is the only caller of the TableIdentifier branch of _split_identifier_for_path, so table updates had no coverage for names that need URL encoding. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Matches create_view and register_table: path from _split_identifier_for_path, raw name for the body. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Register the created view under a second name that needs URL encoding, now that View exposes its metadata location (apache#4088). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
acc4f8c to
6af5063
Compare
| assert created_view.metadata_location is not None | ||
| rest_catalog.register_view(registered_view_identifier, created_view.metadata_location) | ||
| assert rest_catalog.view_exists(registered_view_identifier) | ||
| assert sorted(rest_catalog.list_views(namespace)) == sorted([view_identifier, registered_view_identifier]) |
There was a problem hiding this comment.
two views here because one is from create_view, another from register_review
Rationale for this change
Same kind of bug as #4112, but for views.
create_viewandregister_viewput the URL-encoded view name in the JSON body, somy viewgets created asmy%20view. Use the raw name instead, likecreate_tableandregister_table.Also added an integration test that round-trips names with spaces,
+&=, and non-ASCII through the REST fixture.Are these changes tested?
Yes, both fail on
main:pytest tests/catalog/test_rest.py: passedpytest tests/integration/test_catalog.py -m integration -k test_rest_identifiers_with_special_characters: passedAre there any user-facing changes?
Yes, bug fix for view names that need URL encoding.
AI Disclosure: Assisted by GitHub Copilot.