diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java b/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java index b7051d539c2..8e46b10f385 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java +++ b/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java @@ -1011,4 +1011,53 @@ public class _TestUtil { } } + /** + * Returns a random string in the specified length range consisting + * entirely of whitespace characters + * @see #WHITESPACE_CHARACTERS + */ + public static String randomWhitespace(Random r, int minLength, int maxLength) { + final int end = nextInt(r, minLength, maxLength); + StringBuilder out = new StringBuilder(); + for (int i = 0; i < end; i++) { + int offset = nextInt(r, 0, WHITESPACE_CHARACTERS.length-1); + char c = WHITESPACE_CHARACTERS[offset]; + // sanity check + Assert.assertTrue("Not really whitespace? (@"+offset+"): " + c, Character.isWhitespace(c)); + out.append(c); + } + return out.toString(); + } + + /** List of characters that match {@link Character#isWhitespace} */ + public static final char[] WHITESPACE_CHARACTERS = new char[] { + // :TODO: is this list exhaustive? + '\u0009', + '\n', + '\u000B', + '\u000C', + '\r', + '\u001C', + '\u001D', + '\u001E', + '\u001F', + '\u0020', + // '\u0085', faild sanity check? + '\u1680', + '\u180E', + '\u2000', + '\u2001', + '\u2002', + '\u2003', + '\u2004', + '\u2005', + '\u2006', + '\u2008', + '\u2009', + '\u200A', + '\u2028', + '\u2029', + '\u205F', + '\u3000', + }; } diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 479d789a56b..1d69298aec8 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -406,6 +406,8 @@ Bug Fixes * SOLR-3903: Fixed MissingFormatArgumentException in ConcurrentUpdateSolrServer (hossman) +* SOLR-3916: Fixed whitespace bug in parsing the fl param (hossman) + Other Changes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/search/ReturnFields.java b/solr/core/src/java/org/apache/solr/search/ReturnFields.java index dcc442043ed..a6820aa41cf 100644 --- a/solr/core/src/java/org/apache/solr/search/ReturnFields.java +++ b/solr/core/src/java/org/apache/solr/search/ReturnFields.java @@ -201,7 +201,7 @@ public class ReturnFields sp.eatws(); start = sp.pos; } else { - if (ch==' ' || ch == ',' || ch==0) { + if (Character.isWhitespace(ch) || ch == ',' || ch==0) { addField( field, key, augmenters, req ); continue; } @@ -215,7 +215,7 @@ public class ReturnFields // we read "key : " field = sp.getId(null); ch = sp.ch(); - if (field != null && (ch==' ' || ch == ',' || ch==0)) { + if (field != null && (Character.isWhitespace(ch) || ch == ',' || ch==0)) { rename.add(field, key); addField( field, key, augmenters, req ); continue; @@ -231,7 +231,7 @@ public class ReturnFields field = sp.getGlobbedId(null); ch = sp.ch(); - if (field != null && (ch==' ' || ch == ',' || ch==0)) { + if (field != null && (Character.isWhitespace(ch) || ch == ',' || ch==0)) { // "*" looks and acts like a glob, but we give it special treatment if ("*".equals(field)) { _wantsAllFields = true; diff --git a/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java b/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java index cc57cdb8f76..0ff673f836c 100644 --- a/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java +++ b/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java @@ -21,9 +21,13 @@ import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.params.CommonParams; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.transform.*; + +import org.apache.lucene.util._TestUtil; + import org.junit.BeforeClass; import org.junit.Test; +import java.util.Random; public class ReturnFieldsTest extends SolrTestCaseJ4 { @@ -302,4 +306,38 @@ public class ReturnFieldsTest extends SolrTestCaseJ4 { assertFalse(rf.wantsField("xxx")); assertFalse(rf.wantsAllFields()); } + + public void testWhitespace() { + Random r = random(); + final int iters = atLeast(30); + + for (int i = 0; i < iters; i++) { + final boolean aliasId = r.nextBoolean(); + final boolean aliasFoo = r.nextBoolean(); + + final String id = _TestUtil.randomWhitespace(r, 0, 3) + + (aliasId ? "aliasId:" : "") + + "id" + + _TestUtil.randomWhitespace(r, 1, 3); + final String foo_i = _TestUtil.randomWhitespace(r, 0, 3) + + (aliasFoo ? "aliasFoo:" : "") + + "foo_i" + + _TestUtil.randomWhitespace(r, 0, 3); + + final String fl = id + (r.nextBoolean() ? "" : ",") + foo_i; + ReturnFields rf = new ReturnFields(req("fl", fl)); + + assertFalse("score ("+fl+")", rf.wantsScore()); + + assertTrue("id ("+fl+")", rf.wantsField("id")); + assertTrue("foo_i ("+fl+")", rf.wantsField("foo_i")); + + assertEquals("aliasId ("+fl+")", aliasId, rf.wantsField("aliasId")); + assertEquals("aliasFoo ("+fl+")", aliasFoo, rf.wantsField("aliasFoo")); + + assertFalse(rf.wantsField("xxx")); + assertFalse(rf.wantsAllFields()); + } + } + }