Skip to content

Core: Fix namespace URL encoding to use %20 instead of + for spaces - #15948

Closed
osscm wants to merge 1 commit into
apache:mainfrom
osscm:fix-namespace-url-encoding
Closed

osscm wants to merge 1 commit into
apache:mainfrom
osscm:fix-namespace-url-encoding

Conversation

@osscm

@osscm osscm commented Apr 12, 2026

Copy link
Copy Markdown

RESTUtil.encodeNamespace() called encodeString() which uses Java URLEncoder.

This follows application/x-www-form-urlencoded rules and encodes spaces as +.
While correct for form data and OAuth2, URL path segments require RFC 3986 percent-encoding where spaces must be %20. Servers receiving /v1/namespaces/a+b treat + as a literal character, not a space, causing namespace lookup failures in catalogs such as Polaris.

Fix by replacing + with %20 in encodeNamespace() only, leaving encodeString() unchanged so form data and OAuth2 encoding is unaffected. URLDecoder.decode() already handles both + and %20 as spaces, so decoding remains backward compatible.

Fixes #14263

RESTUtil.encodeNamespace() called encodeString() which uses Java URLEncoder.
This follows application/x-www-form-urlencoded rules and encodes spaces as +.
While correct for form data and OAuth2, URL path segments require RFC 3986
percent-encoding where spaces must be %20. Servers receiving /v1/namespaces/a+b
treat + as a literal character, not a space, causing namespace lookup failures
in catalogs such as Polaris.

Fix by replacing + with %20 in encodeNamespace() only, leaving encodeString()
unchanged so form data and OAuth2 encoding is unaffected. URLDecoder.decode()
already handles both + and %20 as spaces, so decoding remains backward compatible.

Fixes apache#14263
@github-actions github-actions Bot added the core label Apr 12, 2026
@osscm

osscm commented Apr 12, 2026

Copy link
Copy Markdown
Author

cc @nastra @omarsmak please review.

String separator = "%2E";

// single-level namespace with a space
Namespace singleLevel = Namespace.of("a b");

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.

Suggested change
Namespace singleLevel = Namespace.of("a b");
Namespace singleLevel = Namespace.of("my namespace");

@nastra nastra Apr 13, 2026 •

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.

we should also verify encoding/decoding Namespace.of("my+namespace with+spaces") in a separate test to check that the non-encoded namespace with space and a + in its name properly can be encoded/decoded

.as("space must be encoded as %%20, not +")
.isEqualTo("a%20b")
.doesNotContain("+");
assertThat(RESTUtil.decodeNamespace(encoded, separator)).isEqualTo(singleLevel);

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.

we also need to test that decoding the previous way can be properly decoded:

String legacyEncoded = "my+namespace";
assertThat(RESTUtil.decodeNamespace(legacyEncoded, separator)).isEqualTo(singleLevel);

.as("spaces in every level must be encoded as %%20, not +")
.isEqualTo("my%20namespace%2Emy%20schema")
.doesNotContain("+");
assertThat(RESTUtil.decodeNamespace(encodedMulti, separator)).isEqualTo(multiLevel);

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.

same as above, we need to make sure that a legacy encoded namespaces can still be decoded properly


for (int i = 0; i < levels.length; i++) {
encodedLevels[i] = encodeString(levels[i]);
encodedLevels[i] = encodeString(levels[i]).replace("+", "%20");

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.

We also need a test for encodeFormData() to verify application/x-www-form-urlencoded is properly used. Additionally, we need some more tests for all paths being constructed via ResourcePaths

@nastra

nastra commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

we should maybe have a separate

/** Encodes a value for use in a URL path segment (RFC 3986: spaces as %20, not +). */
  public static String encodePathSegment(String toEncode) {
      return encodeString(toEncode).replace("+", "%20");
  }

that is used for namespace and other path encoding from ResourcePaths

@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale due to 30 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@iceberg.apache.org list. Thank you for your contributions.

@github-actions github-actions Bot added the stale label May 14, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been closed due to lack of activity. This is not a judgement on the merit of the PR in any way. It is just a way of keeping the PR queue manageable. If you think that is incorrect, or the pull request requires review, you can revive the PR at any time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Encoding of Namespace having space to +

2 participants