lucene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hoss...@apache.org
Subject [25/35] lucene-solr:jira/SOLR-10834: SOLR-10830: Solr now correctly enforces that the '_root_' field has the same fieldType as the uniqueKey field
Date Wed, 14 Jun 2017 02:03:01 GMT
SOLR-10830: Solr now correctly enforces that the '_root_' field has the same fieldType as the uniqueKey field


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/6396cb75
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/6396cb75
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/6396cb75

Branch: refs/heads/jira/SOLR-10834
Commit: 6396cb759f8c799f381b0730636fa412761030ce
Parents: e503325
Author: Chris Hostetter <hossman@apache.org>
Authored: Tue Jun 13 10:17:32 2017 -0700
Committer: Chris Hostetter <hossman@apache.org>
Committed: Tue Jun 13 10:17:32 2017 -0700

----------------------------------------------------------------------
 solr/CHANGES.txt                                |  4 +
 .../apache/solr/schema/DoublePointField.java    | 10 +--
 .../org/apache/solr/schema/FloatPointField.java | 10 +--
 .../org/apache/solr/schema/IndexSchema.java     | 31 ++++++-
 .../org/apache/solr/schema/IntPointField.java   | 10 +--
 .../org/apache/solr/schema/LongPointField.java  | 10 +--
 .../apache/solr/schema/NumericFieldType.java    | 88 +++++++++++++++++---
 .../java/org/apache/solr/schema/PointField.java |  7 +-
 .../java/org/apache/solr/schema/TrieField.java  | 60 ++++++-------
 .../apache/solr/update/AddUpdateCommand.java    |  8 +-
 .../solr/update/DirectUpdateHandler2.java       |  3 +-
 .../org/apache/solr/update/DocumentBuilder.java |  8 +-
 ...-schema-uniquekey-diff-type-dynamic-root.xml | 36 ++++++++
 .../bad-schema-uniquekey-diff-type-root.xml     | 35 ++++++++
 .../test-files/solr/collection1/conf/schema.xml | 16 +++-
 .../solr/collection1/conf/schema11.xml          |  4 +
 .../solr/collection1/conf/schema12.xml          | 23 ++++-
 .../solr/configsets/cloud-hdfs/conf/schema.xml  |  2 +-
 .../conf/managed-schema                         |  2 +-
 .../cloud-managed-upgrade/conf/schema.xml       |  2 +-
 .../cloud-managed/conf/managed-schema           |  2 +-
 .../cloud-minimal-jmx/conf/schema.xml           |  2 +-
 .../configsets/cloud-minimal/conf/schema.xml    |  2 +-
 .../configsets/cloud-subdirs/conf/schema.xml    |  2 +-
 .../exitable-directory/conf/schema.xml          |  2 +-
 .../org/apache/solr/BasicFunctionalityTest.java | 77 ++++++++++++++++-
 .../apache/solr/schema/BadIndexSchemaTest.java  | 15 ++++
 .../org/apache/solr/search/TestQueryTypes.java  | 46 +++++++++-
 .../apache/solr/search/TestSolrQueryParser.java | 36 ++++++++
 29 files changed, 467 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index d2a26c0..c1c7293 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -156,6 +156,10 @@ Bug Fixes
 
 * SOLR-10223: Allow running examples as root on Linux with -force option (janhoy)
 
+* SOLR-10830: Solr now correctly enforces that the '_root_' field has the same fieldType as the
+  uniqueKey field.  With out this enforcement, child document updating was unreliable. (hossman)
+
+
 Optimizations
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
index 05a1ce7..d2cf6ed 100644
--- a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
@@ -61,7 +61,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
     if (min == null) {
       actualMin = Double.NEGATIVE_INFINITY;
     } else {
-      actualMin = Double.parseDouble(min);
+      actualMin = parseDoubleFromUser(field.getName(), min);
       if (!minInclusive) {
         actualMin = DoublePoint.nextUp(actualMin);
       }
@@ -69,7 +69,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
     if (max == null) {
       actualMax = Double.POSITIVE_INFINITY;
     } else {
-      actualMax = Double.parseDouble(max);
+      actualMax = parseDoubleFromUser(field.getName(), max);
       if (!maxInclusive) {
         actualMax = DoublePoint.nextDown(actualMax);
       }
@@ -100,7 +100,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
 
   @Override
   protected Query getExactQuery(SchemaField field, String externalVal) {
-    return DoublePoint.newExactQuery(field.getName(), Double.parseDouble(externalVal));
+    return DoublePoint.newExactQuery(field.getName(), parseDoubleFromUser(field.getName(), externalVal));
   }
 
   @Override
@@ -112,7 +112,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
     double[] values = new double[externalVal.size()];
     int i = 0;
     for (String val:externalVal) {
-      values[i] = Double.parseDouble(val);
+      values[i] = parseDoubleFromUser(field.getName(), val);
       i++;
     }
     return DoublePoint.newSetQuery(field.getName(), values);
@@ -127,7 +127,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
   public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
     result.grow(Double.BYTES);
     result.setLength(Double.BYTES);
-    DoublePoint.encodeDimension(Double.parseDouble(val.toString()), result.bytes(), 0);
+    DoublePoint.encodeDimension(parseDoubleFromUser(null, val.toString()), result.bytes(), 0);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
index fe9c753..e1a9741 100644
--- a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
@@ -61,7 +61,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
     if (min == null) {
       actualMin = Float.NEGATIVE_INFINITY;
     } else {
-      actualMin = Float.parseFloat(min);
+      actualMin = parseFloatFromUser(field.getName(), min);
       if (!minInclusive) {
         actualMin = FloatPoint.nextUp(actualMin);
       }
@@ -69,7 +69,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
     if (max == null) {
       actualMax = Float.POSITIVE_INFINITY;
     } else {
-      actualMax = Float.parseFloat(max);
+      actualMax = parseFloatFromUser(field.getName(), max);
       if (!maxInclusive) {
         actualMax = FloatPoint.nextDown(actualMax);
       }
@@ -100,7 +100,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
 
   @Override
   protected Query getExactQuery(SchemaField field, String externalVal) {
-    return FloatPoint.newExactQuery(field.getName(), Float.parseFloat(externalVal));
+    return FloatPoint.newExactQuery(field.getName(), parseFloatFromUser(field.getName(), externalVal));
   }
 
   @Override
@@ -112,7 +112,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
     float[] values = new float[externalVal.size()];
     int i = 0;
     for (String val:externalVal) {
-      values[i] = Float.parseFloat(val);
+      values[i] = parseFloatFromUser(field.getName(), val);
       i++;
     }
     return FloatPoint.newSetQuery(field.getName(), values);
@@ -127,7 +127,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
   public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
     result.grow(Float.BYTES);
     result.setLength(Float.BYTES);
-    FloatPoint.encodeDimension(Float.parseFloat(val.toString()), result.bytes(), 0);
+    FloatPoint.encodeDimension(parseFloatFromUser(null, val.toString()), result.bytes(), 0);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
index 463df3e..36efbcf 100644
--- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
@@ -113,6 +113,7 @@ public class IndexSchema {
   public static final String SOURCE = "source";
   public static final String TYPE = "type";
   public static final String TYPES = "types";
+  public static final String ROOT_FIELD_NAME = "_root_";
   public static final String UNIQUE_KEY = "uniqueKey";
   public static final String VERSION = "version";
 
@@ -517,6 +518,20 @@ public class IndexSchema {
         log.warn("no " + UNIQUE_KEY + " specified in schema.");
       } else {
         uniqueKeyField=getIndexedField(node.getNodeValue().trim());
+        uniqueKeyFieldName=uniqueKeyField.getName();
+        uniqueKeyFieldType=uniqueKeyField.getType();
+        
+        // we fail on init if the ROOT field is *explicitly* defined as incompatible with uniqueKey
+        // we don't want ot fail if there happens to be a dynamicField matching ROOT, (ie: "*")
+        // because the user may not care about child docs at all.  The run time code
+        // related to child docs can catch that if it happens
+        if (fields.containsKey(ROOT_FIELD_NAME) && ! isUsableForChildDocs()) {
+          String msg = ROOT_FIELD_NAME + " field must be defined using the exact same fieldType as the " +
+            UNIQUE_KEY + " field ("+uniqueKeyFieldName+") uses: " + uniqueKeyFieldType.getTypeName();
+          log.error(msg);
+          throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+        }
+        
         if (null != uniqueKeyField.getDefaultValue()) {
           String msg = UNIQUE_KEY + " field ("+uniqueKeyFieldName+
               ") can not be configured with a default value ("+
@@ -542,9 +557,6 @@ public class IndexSchema {
           throw new SolrException(ErrorCode.SERVER_ERROR, msg);
         }
         
-        uniqueKeyFieldName=uniqueKeyField.getName();
-        uniqueKeyFieldType=uniqueKeyField.getType();
-
         // Unless the uniqueKeyField is marked 'required=false' then make sure it exists
         if( Boolean.FALSE != explicitRequiredProp.get( uniqueKeyFieldName ) ) {
           uniqueKeyField.required = true;
@@ -1914,4 +1926,17 @@ public class IndexSchema {
         + XPATH_OR + stepsToPath(SCHEMA, TYPES, FIELD_TYPE);
     return expression;
   }
+
+  /**
+   * Helper method that returns <code>true</code> if the {@link #ROOT_FIELD_NAME} uses the exact 
+   * same 'type' as the {@link #getUniqueKeyField()}
+   *
+   * @lucene.internal
+   */
+  public boolean isUsableForChildDocs() {
+    FieldType rootType = getFieldType(ROOT_FIELD_NAME);
+    return (null != uniqueKeyFieldType &&
+            null != rootType &&
+            rootType.getTypeName().equals(uniqueKeyFieldType.getTypeName()));
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/IntPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/IntPointField.java b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
index f47f450..7d36612 100644
--- a/solr/core/src/java/org/apache/solr/schema/IntPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
@@ -64,7 +64,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
     if (min == null) {
       actualMin = Integer.MIN_VALUE;
     } else {
-      actualMin = Integer.parseInt(min);
+      actualMin = parseIntFromUser(field.getName(), min);
       if (!minInclusive) {
         actualMin++;
       }
@@ -72,7 +72,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
     if (max == null) {
       actualMax = Integer.MAX_VALUE;
     } else {
-      actualMax = Integer.parseInt(max);
+      actualMax = parseIntFromUser(field.getName(), max);
       if (!maxInclusive) {
         actualMax--;
       }
@@ -97,7 +97,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
 
   @Override
   protected Query getExactQuery(SchemaField field, String externalVal) {
-    return IntPoint.newExactQuery(field.getName(), Integer.parseInt(externalVal));
+    return IntPoint.newExactQuery(field.getName(), parseIntFromUser(field.getName(), externalVal));
   }
   
   @Override
@@ -109,7 +109,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
     int[] values = new int[externalVal.size()];
     int i = 0;
     for (String val:externalVal) {
-      values[i] = Integer.parseInt(val);
+      values[i] = parseIntFromUser(field.getName(), val);
       i++;
     }
     return IntPoint.newSetQuery(field.getName(), values);
@@ -124,7 +124,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
   public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
     result.grow(Integer.BYTES);
     result.setLength(Integer.BYTES);
-    IntPoint.encodeDimension(Integer.parseInt(val.toString()), result.bytes(), 0);
+    IntPoint.encodeDimension(parseIntFromUser(null, val.toString()), result.bytes(), 0);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/LongPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/LongPointField.java b/solr/core/src/java/org/apache/solr/schema/LongPointField.java
index bef6c47..b2d8a3a 100644
--- a/solr/core/src/java/org/apache/solr/schema/LongPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/LongPointField.java
@@ -63,7 +63,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
     if (min == null) {
       actualMin = Long.MIN_VALUE;
     } else {
-      actualMin = Long.parseLong(min);
+      actualMin = parseLongFromUser(field.getName(), min);
       if (!minInclusive) {
         actualMin++;
       }
@@ -71,7 +71,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
     if (max == null) {
       actualMax = Long.MAX_VALUE;
     } else {
-      actualMax = Long.parseLong(max);
+      actualMax = parseLongFromUser(field.getName(), max);
       if (!maxInclusive) {
         actualMax--;
       }
@@ -96,7 +96,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
 
   @Override
   protected Query getExactQuery(SchemaField field, String externalVal) {
-    return LongPoint.newExactQuery(field.getName(), Long.parseLong(externalVal));
+    return LongPoint.newExactQuery(field.getName(), parseLongFromUser(field.getName(), externalVal));
   }
   
   @Override
@@ -108,7 +108,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
     long[] values = new long[externalVal.size()];
     int i = 0;
     for (String val:externalVal) {
-      values[i] = Long.parseLong(val);
+      values[i] = parseLongFromUser(field.getName(), val);
       i++;
     }
     return LongPoint.newSetQuery(field.getName(), values);
@@ -123,7 +123,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
   public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
     result.grow(Long.BYTES);
     result.setLength(Long.BYTES);
-    LongPoint.encodeDimension(Long.parseLong(val.toString()), result.bytes(), 0);
+    LongPoint.encodeDimension(parseLongFromUser(null, val.toString()), result.bytes(), 0);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java b/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
index 6cda9ca..cf17aaf 100644
--- a/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
@@ -56,8 +56,8 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
     switch (getNumberType()) {
       case INTEGER:
         return numericDocValuesRangeQuery(field.getName(),
-              min == null ? null : (long) Integer.parseInt(min),
-              max == null ? null : (long) Integer.parseInt(max),
+              min == null ? null : (long) parseIntFromUser(field.getName(), min),
+              max == null ? null : (long) parseIntFromUser(field.getName(), max),
               minInclusive, maxInclusive, field.multiValued());
       case FLOAT:
         if (field.multiValued()) {
@@ -67,8 +67,8 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
         }
       case LONG:
         return numericDocValuesRangeQuery(field.getName(),
-              min == null ? null : Long.parseLong(min),
-              max == null ? null : Long.parseLong(max),
+              min == null ? null : parseLongFromUser(field.getName(), min),
+              max == null ? null : parseLongFromUser(field.getName(),max),
               minInclusive, maxInclusive, field.multiValued());
       case DOUBLE:
         if (field.multiValued()) { 
@@ -90,8 +90,8 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
     Query query;
     String fieldName = sf.getName();
 
-    Number minVal = min == null ? null : getNumberType() == NumberType.FLOAT ? Float.parseFloat(min): Double.parseDouble(min);
-    Number maxVal = max == null ? null : getNumberType() == NumberType.FLOAT ? Float.parseFloat(max): Double.parseDouble(max);
+    Number minVal = min == null ? null : getNumberType() == NumberType.FLOAT ? parseFloatFromUser(sf.getName(), min): parseDoubleFromUser(sf.getName(), min);
+    Number maxVal = max == null ? null : getNumberType() == NumberType.FLOAT ? parseFloatFromUser(sf.getName(), max): parseDoubleFromUser(sf.getName(), max);
     
     Long minBits = 
         min == null ? null : getNumberType() == NumberType.FLOAT ? (long) Float.floatToIntBits(minVal.floatValue()): Double.doubleToLongBits(minVal.doubleValue());
@@ -124,14 +124,14 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
   }
   
   protected Query getRangeQueryForMultiValuedDoubleDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
-    Long minBits = min == null ? NumericUtils.doubleToSortableLong(Double.NEGATIVE_INFINITY): NumericUtils.doubleToSortableLong(Double.parseDouble(min));
-    Long maxBits = max == null ? NumericUtils.doubleToSortableLong(Double.POSITIVE_INFINITY): NumericUtils.doubleToSortableLong(Double.parseDouble(max));
+    Long minBits = min == null ? NumericUtils.doubleToSortableLong(Double.NEGATIVE_INFINITY): NumericUtils.doubleToSortableLong(parseDoubleFromUser(sf.getName(), min));
+    Long maxBits = max == null ? NumericUtils.doubleToSortableLong(Double.POSITIVE_INFINITY): NumericUtils.doubleToSortableLong(parseDoubleFromUser(sf.getName(), max));
     return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
   }
   
   protected Query getRangeQueryForMultiValuedFloatDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
-    Long minBits = (long)(min == null ? NumericUtils.floatToSortableInt(Float.NEGATIVE_INFINITY): NumericUtils.floatToSortableInt(Float.parseFloat(min)));
-    Long maxBits = (long)(max == null ? NumericUtils.floatToSortableInt(Float.POSITIVE_INFINITY): NumericUtils.floatToSortableInt(Float.parseFloat(max)));
+    Long minBits = (long)(min == null ? NumericUtils.floatToSortableInt(Float.NEGATIVE_INFINITY): NumericUtils.floatToSortableInt(parseFloatFromUser(sf.getName(), min)));
+    Long maxBits = (long)(max == null ? NumericUtils.floatToSortableInt(Float.POSITIVE_INFINITY): NumericUtils.floatToSortableInt(parseFloatFromUser(sf.getName(), max)));
     return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
   }
   
@@ -169,4 +169,72 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
       return NumericDocValuesField.newRangeQuery(field, actualLowerValue, actualUpperValue);
     }
   }
+  
+  /** 
+   * Wrapper for {@link Long#parseLong(String)} that throws a BAD_REQUEST error if the input is not valid 
+   * @param fieldName used in any exception, may be null
+   * @param val string to parse, NPE if null
+   */
+  static long parseLongFromUser(String fieldName, String val) {
+    if (val == null) {
+      throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+    }
+    try {
+      return Long.parseLong(val);
+    } catch (NumberFormatException e) {
+      String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+    }
+  }
+  
+  /** 
+   * Wrapper for {@link Integer#parseInt(String)} that throws a BAD_REQUEST error if the input is not valid 
+   * @param fieldName used in any exception, may be null
+   * @param val string to parse, NPE if null
+   */
+  static int parseIntFromUser(String fieldName, String val) {
+    if (val == null) {
+      throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+    }
+    try {
+      return Integer.parseInt(val);
+    } catch (NumberFormatException e) {
+      String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+    }
+  }
+  
+  /** 
+   * Wrapper for {@link Double#parseDouble(String)} that throws a BAD_REQUEST error if the input is not valid 
+   * @param fieldName used in any exception, may be null
+   * @param val string to parse, NPE if null
+   */
+  static double parseDoubleFromUser(String fieldName, String val) {
+    if (val == null) {
+      throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+    }
+    try {
+      return Double.parseDouble(val);
+    } catch (NumberFormatException e) {
+      String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+    }
+  }
+  
+  /** 
+   * Wrapper for {@link Float#parseFloat(String)} that throws a BAD_REQUEST error if the input is not valid 
+   * @param fieldName used in any exception, may be null
+   * @param val string to parse, NPE if null
+   */
+  static float parseFloatFromUser(String fieldName, String val) {
+    if (val == null) {
+      throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+    }
+    try {
+      return Float.parseFloat(val);
+    } catch (NumberFormatException e) {
+      String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/PointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/PointField.java b/solr/core/src/java/org/apache/solr/schema/PointField.java
index cad3c7e..98105af 100644
--- a/solr/core/src/java/org/apache/solr/schema/PointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/PointField.java
@@ -127,7 +127,7 @@ public abstract class PointField extends NumericFieldType {
       return new IndexOrDocValuesQuery(pointsQuery, dvQuery);
     } else {
       return getExactQuery(field, externalVal);
-    }
+    } 
   }
 
   protected abstract Query getExactQuery(SchemaField field, String externalVal);
@@ -191,6 +191,11 @@ public abstract class PointField extends NumericFieldType {
   
   protected abstract String indexedToReadable(BytesRef indexedForm);
   
+  @Override
+  public Query getPrefixQuery(QParser parser, SchemaField sf, String termStr) {
+    throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't run prefix queries on numeric fields");
+  }
+  
   protected boolean isFieldUsed(SchemaField field) {
     boolean indexed = field.indexed();
     boolean stored = field.stored();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/schema/TrieField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieField.java b/solr/core/src/java/org/apache/solr/schema/TrieField.java
index f90877c..998583b 100644
--- a/solr/core/src/java/org/apache/solr/schema/TrieField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieField.java
@@ -344,7 +344,7 @@ public class TrieField extends NumericFieldType {
     }
     int ps = precisionStep;
     Query query;
-
+    
     if (field.hasDocValues() && !field.indexed()) {
       return getDocValuesRangeQuery(parser, field, min, max, minInclusive, maxInclusive);
     }
@@ -352,26 +352,26 @@ public class TrieField extends NumericFieldType {
     switch (type) {
       case INTEGER:
         query = LegacyNumericRangeQuery.newIntRange(field.getName(), ps,
-            min == null ? null : Integer.parseInt(min),
-            max == null ? null : Integer.parseInt(max),
+            min == null ? null : parseIntFromUser(field.getName(), min),
+            max == null ? null : parseIntFromUser(field.getName(), max),
             minInclusive, maxInclusive);
         break;
       case FLOAT:
         query = LegacyNumericRangeQuery.newFloatRange(field.getName(), ps,
-            min == null ? null : Float.parseFloat(min),
-            max == null ? null : Float.parseFloat(max),
+            min == null ? null : parseFloatFromUser(field.getName(), min),
+            max == null ? null : parseFloatFromUser(field.getName(), max),
             minInclusive, maxInclusive);
         break;
       case LONG:
         query = LegacyNumericRangeQuery.newLongRange(field.getName(), ps,
-            min == null ? null : Long.parseLong(min),
-            max == null ? null : Long.parseLong(max),
+            min == null ? null : parseLongFromUser(field.getName(), min),
+            max == null ? null : parseLongFromUser(field.getName(), max),
             minInclusive, maxInclusive);
         break;
       case DOUBLE:
         query = LegacyNumericRangeQuery.newDoubleRange(field.getName(), ps,
-            min == null ? null : Double.parseDouble(min),
-            max == null ? null : Double.parseDouble(max),
+            min == null ? null : parseDoubleFromUser(field.getName(), min),
+            max == null ? null : parseDoubleFromUser(field.getName(), max),
             minInclusive, maxInclusive);
         break;
       case DATE:
@@ -383,7 +383,6 @@ public class TrieField extends NumericFieldType {
       default:
         throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field");
     }
-
     return query;
   }
 
@@ -413,29 +412,24 @@ public class TrieField extends NumericFieldType {
   @Override
   public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
     String s = val.toString();
-    try {
-      switch (type) {
-        case INTEGER:
-          LegacyNumericUtils.intToPrefixCoded(Integer.parseInt(s), 0, result);
-          break;
-        case FLOAT:
-          LegacyNumericUtils.intToPrefixCoded(NumericUtils.floatToSortableInt(Float.parseFloat(s)), 0, result);
-          break;
-        case LONG:
-          LegacyNumericUtils.longToPrefixCoded(Long.parseLong(s), 0, result);
-          break;
-        case DOUBLE:
-          LegacyNumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(Double.parseDouble(s)), 0, result);
-          break;
-        case DATE:
-          LegacyNumericUtils.longToPrefixCoded(DateMathParser.parseMath(null, s).getTime(), 0, result);
-          break;
-        default:
-          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
-      }
-    } catch (NumberFormatException nfe) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, 
-                              "Invalid Number: " + val);
+    switch (type) {
+      case INTEGER:
+        LegacyNumericUtils.intToPrefixCoded(parseIntFromUser(null, s), 0, result);
+        break;
+      case FLOAT:
+        LegacyNumericUtils.intToPrefixCoded(NumericUtils.floatToSortableInt(parseFloatFromUser(null, s)), 0, result);
+        break;
+      case LONG:
+        LegacyNumericUtils.longToPrefixCoded(parseLongFromUser(null, s), 0, result);
+        break;
+      case DOUBLE:
+        LegacyNumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(parseDoubleFromUser(null, s)), 0, result);
+        break;
+      case DATE:
+        LegacyNumericUtils.longToPrefixCoded(DateMathParser.parseMath(null, s).getTime(), 0, result);
+        break;
+      default:
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java b/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
index 596ddd3..cb1af9a 100644
--- a/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
+++ b/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
@@ -191,7 +191,7 @@ public class AddUpdateCommand extends UpdateCommand implements Iterable<Document
         boolean isVersion = version != 0;
 
         for (SolrInputDocument sdoc : all) {
-          sdoc.setField("_root_", idField);      // should this be a string or the same type as the ID?
+          sdoc.setField(IndexSchema.ROOT_FIELD_NAME, idField);
           if(isVersion) sdoc.setField(CommonParams.VERSION_FIELD, version);
           // TODO: if possible concurrent modification exception (if SolrInputDocument not cloned and is being forwarded to replicas)
           // then we could add this field to the generated lucene document instead.
@@ -220,6 +220,12 @@ public class AddUpdateCommand extends UpdateCommand implements Iterable<Document
   private List<SolrInputDocument> flatten(SolrInputDocument root) {
     List<SolrInputDocument> unwrappedDocs = new ArrayList<>();
     recUnwrapp(unwrappedDocs, root);
+    if (1 < unwrappedDocs.size() && ! req.getSchema().isUsableForChildDocs()) {
+      throw new SolrException
+        (SolrException.ErrorCode.BAD_REQUEST, "Unable to index docs with children: the schema must " +
+         "include definitions for both a uniqueKey field and the '" + IndexSchema.ROOT_FIELD_NAME +
+         "' field, using the exact same fieldType");
+    }
     return unwrappedDocs;
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
index e481109..3efb748 100644
--- a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
+++ b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
@@ -55,6 +55,7 @@ import org.apache.solr.request.LocalSolrQueryRequest;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
 import org.apache.solr.search.FunctionRangeQuery;
 import org.apache.solr.search.QParser;
@@ -394,7 +395,7 @@ public class DirectUpdateHandler2 extends UpdateHandler implements SolrCoreState
   }
 
   private Term getIdTerm(AddUpdateCommand cmd) {
-    return new Term(cmd.isBlock() ? "_root_" : idField.getName(), cmd.getIndexedId());
+    return new Term(cmd.isBlock() ? IndexSchema.ROOT_FIELD_NAME : idField.getName(), cmd.getIndexedId());
   }
 
   private void updateDeleteTrackers(DeleteUpdateCommand cmd) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java
index b97af3b..58638ae 100644
--- a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java
+++ b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java
@@ -176,8 +176,8 @@ public class DocumentBuilder {
                 // check if the copy field is a multivalued or not
                 if (!destinationField.multiValued() && destHasValues) {
                   throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
-                      "ERROR: "+getID(doc, schema)+"multiple values encountered for non multiValued copy field " +
-                          destinationField.getName() + ": " + v);
+                      "Multiple values encountered for non multiValued copy field " +
+                      destinationField.getName() + ": " + v);
                 }
 
                 used = true;
@@ -198,7 +198,9 @@ public class DocumentBuilder {
         }
       }
       catch( SolrException ex ) {
-        throw ex;
+        throw new SolrException(SolrException.ErrorCode.getErrorCode(ex.code()),
+            "ERROR: "+getID(doc, schema)+"Error adding field '" + 
+              field.getName() + "'='" +field.getValue()+"' msg=" + ex.getMessage(), ex );
       }
       catch( Exception ex ) {
         throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-dynamic-root.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-dynamic-root.xml b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-dynamic-root.xml
new file mode 100644
index 0000000..4f06bd1
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-dynamic-root.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<schema name="bad-schema-uniquekey-diff-type-dynamic-root" version="1.4">
+  <!-- NOTE: these fieldTypes are defined with same class, and same props, but schema should
+       still error because the type *names* used not identical.
+       this is risky, because it means the types could diverge over time, and we wnat to
+       protect the user fromthat.
+  -->
+  <fieldType name="string1" class="solr.StrField"/>
+  <fieldType name="string2" class="solr.StrField"/>
+
+  <uniqueKey>id</uniqueKey>
+  <field name="id" type="string1" indexed="true" stored="true" />
+
+  <!-- BEGIN BAD STUFF -->
+  <!-- matches '_root_' -->
+  <dynamicField name="*" type="string2" indexed="true" stored="true" />
+  <!-- END BAD STUFF -->
+
+</schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-root.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-root.xml b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-root.xml
new file mode 100644
index 0000000..377762d
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-root.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<schema name="bad-schema-uniquekey-diff-type-root" version="1.4">
+  <!-- NOTE: these fieldTypes are defined with same class, and same props, but schema should
+       still error because the type *names* used not identical.
+       this is risky, because it means the types could diverge over time, and we wnat to
+       protect the user fromthat.
+  -->
+  <fieldType name="string1" class="solr.StrField"/>
+  <fieldType name="string2" class="solr.StrField"/>
+
+  <uniqueKey>id</uniqueKey>
+  <field name="id" type="string1" indexed="true" stored="true" />
+
+  <!-- BEGIN BAD STUFF -->
+  <field name="_root_" type="string2" indexed="true" stored="true" />
+  <!-- END BAD STUFF -->
+
+</schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/collection1/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema.xml b/solr/core/src/test-files/solr/collection1/conf/schema.xml
index 05145f9..f0ddae4 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema.xml
@@ -734,11 +734,25 @@
                 useDocValuesAsStored="true"/>
   <dynamicField name="*_s_dvo" multiValued="false" type="string" docValues="true" indexed="false" stored="false"
                 useDocValuesAsStored="true"/>
-
+  <dynamicField name="*_l_dvo" multiValued="false" type="${solr.tests.longClass:plong}" docValues="true" indexed="false" stored="false"
+                useDocValuesAsStored="true"/>
+  <dynamicField name="*_f_dvo" multiValued="false" type="${solr.tests.floatClass:pfloat}" docValues="true" indexed="false" stored="false"
+                useDocValuesAsStored="true"/>
+  <dynamicField name="*_dt_dvo" multiValued="false" type="${solr.tests.dateClass:pdate}" docValues="true" indexed="false" stored="false"
+                useDocValuesAsStored="true"/>
+                
   <dynamicField name="*_ii_dvo" multiValued="true" type="${solr.tests.intClass:pint}" docValues="true" indexed="false" stored="false"
                 useDocValuesAsStored="true"/>
   <dynamicField name="*_dd_dvo" multiValued="true" type="${solr.tests.doubleClass:pdouble}" docValues="true" indexed="false" stored="false"
                 useDocValuesAsStored="true"/>
+                
+                
+  <!-- Only Stored numerics -->
+  <dynamicField name="*_i_os" type="${solr.tests.intClass:pint}" indexed="false" stored="true" docValues="false"/>
+  <dynamicField name="*_l_os" type="${solr.tests.longClass:plong}" indexed="false" stored="true" docValues="false"/>
+  <dynamicField name="*_f_os" type="${solr.tests.floatClass:pfloat}" indexed="false" stored="true" docValues="false"/>
+  <dynamicField name="*_d_os" type="${solr.tests.doubleClass:pdouble}" indexed="false" stored="true" docValues="false"/>
+  <dynamicField name="*_dt_os" type="${solr.tests.dateClass:pdate}" indexed="false" stored="true" docValues="false"/>
 
   <!--  Non-stored, DocValues=true, useDocValuesAsStored=false -->
   <field name="single_i_dvn" multiValued="false" type="${solr.tests.intClass:pint}" indexed="true" stored="true"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/collection1/conf/schema11.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema11.xml b/solr/core/src/test-files/solr/collection1/conf/schema11.xml
index caa24cc..819b6d1 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema11.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema11.xml
@@ -413,6 +413,10 @@ valued. -->
    <dynamicField name="*_ds_p"      type="pdouble"    indexed="true"  stored="true" docValues="true" multiValued="true"/>
    <dynamicField name="*_d_ni_p"   type="pdouble"    indexed="false"  stored="true" docValues="true"/>
    <dynamicField name="*_ds_ni_p"   type="pdouble"    indexed="false"  stored="true" docValues="true" multiValued="true"/>
+   <dynamicField name="*_dt_p"      type="pdate"    indexed="true"  stored="true" docValues="true"/>
+   <dynamicField name="*_dts_p"      type="pdate"    indexed="true"  stored="true" docValues="true" multiValued="true"/>
+   <dynamicField name="*_dt_ni_p"   type="pdate"    indexed="false"  stored="true" docValues="true"/>
+   <dynamicField name="*_dts_ni_p"   type="pdate"    indexed="false"  stored="true" docValues="true" multiValued="true"/>
 
    <dynamicField name="*_t"  type="text"    indexed="true"  stored="true"/>
    <dynamicField name="*_b"  type="boolean" indexed="true"  stored="true"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/collection1/conf/schema12.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema12.xml b/solr/core/src/test-files/solr/collection1/conf/schema12.xml
index 214fc26..2236395 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema12.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema12.xml
@@ -604,16 +604,33 @@
   <dynamicField name="*_is" type="${solr.tests.intClass:pint}" indexed="true" stored="true" multiValued="true"/>
   <dynamicField name="*_i_dv" type="${solr.tests.intClass:pint}" indexed="true" stored="true" docValues="true" multiValued="false"/>
   <dynamicField name="*_is_dv" type="${solr.tests.intClass:pint}" indexed="true" stored="true" docValues="true" multiValued="true"/>
+  <dynamicField name="*_i_dvo" type="${solr.tests.intClass:pint}" indexed="false" stored="true" docValues="true"/>
+  
+  <dynamicField name="*_f" type="${solr.tests.floatClass:pfloat}" indexed="true" stored="true"/>
+  <dynamicField name="*_fs" type="${solr.tests.floatClass:pfloat}" indexed="true" stored="true" multiValued="true"/>
+  <dynamicField name="*_f_dv" type="${solr.tests.floatClass:pfloat}" indexed="true" stored="true" docValues="true" multiValued="false"/>
+  <dynamicField name="*_fs_dv" type="${solr.tests.floatClass:pfloat}" indexed="true" stored="true" docValues="true" multiValued="true"/>
+  <dynamicField name="*_f_dvo" type="${solr.tests.floatClass:pfloat}" indexed="false" stored="true" docValues="true"/>
+  
+  <dynamicField name="*_l" type="${solr.tests.longClass:plong}" indexed="true" stored="true"/>
+  <dynamicField name="*_ls" type="${solr.tests.longClass:plong}" indexed="true" stored="true" multiValued="true"/>
+  <dynamicField name="*_l_dv" type="${solr.tests.longClass:plong}" indexed="true" stored="true" docValues="true" multiValued="false"/>
+  <dynamicField name="*_ls_dv" type="${solr.tests.longClass:plong}" indexed="true" stored="true" docValues="true" multiValued="true"/>
+  <dynamicField name="*_l_dvo" type="${solr.tests.longClass:plong}" indexed="false" stored="true" docValues="true"/>
+  
+  <dynamicField name="*_d" type="${solr.tests.doubleClass:pdouble}" indexed="true" stored="true"/>
+  <dynamicField name="*_ds" type="${solr.tests.doubleClass:pdouble}" indexed="true" stored="true" multiValued="true"/>
+  <dynamicField name="*_d_dv" type="${solr.tests.doubleClass:pdouble}" indexed="true" stored="true" docValues="true" multiValued="false"/>
+  <dynamicField name="*_ds_dv" type="${solr.tests.doubleClass:pdouble}" indexed="true" stored="true" docValues="true" multiValued="true"/>
+  <dynamicField name="*_d_dvo" type="${solr.tests.doubleClass:pdouble}" indexed="false" stored="true" docValues="true"/>
+  
   <dynamicField name="*_s1" type="string" indexed="true" stored="true" multiValued="false"/>
   <!-- :TODO: why are these identical?!?!?! -->
   <dynamicField name="*_s" type="string" indexed="true" stored="true" multiValued="true"/>
   <dynamicField name="*_ss" type="string" indexed="true" stored="true" multiValued="true"/>
-  <dynamicField name="*_l" type="${solr.tests.longClass:plong}" indexed="true" stored="true"/>
   <dynamicField name="*_t" type="text" indexed="true" stored="true"/>
   <dynamicField name="*_tt" type="text" indexed="true" stored="true"/>
   <dynamicField name="*_b" type="boolean" indexed="true" stored="true"/>
-  <dynamicField name="*_f" type="${solr.tests.floatClass:pfloat}" indexed="true" stored="true"/>
-  <dynamicField name="*_d" type="${solr.tests.doubleClass:pdouble}" indexed="true" stored="true"/>
   <dynamicField name="*_dt" type="${solr.tests.dateClass:pdate}" indexed="true" stored="true"/>
 
   <dynamicField name="*_pi" type="pint" indexed="true" stored="true" docValues="false" multiValued="false"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml
index aab5e81..7b8b690 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml
@@ -22,7 +22,7 @@
   <dynamicField name="*" type="string" indexed="true" stored="true"/>
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema b/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema
index e70e02b..39e142e 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema
@@ -35,7 +35,7 @@
 
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml
index b9f09f9..1d97a2a 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml
@@ -21,7 +21,7 @@
   <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
index b9f09f9..1d97a2a 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
@@ -21,7 +21,7 @@
   <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml
index aab5e81..7b8b690 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml
@@ -22,7 +22,7 @@
   <dynamicField name="*" type="string" indexed="true" stored="true"/>
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
index aab5e81..7b8b690 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
@@ -22,7 +22,7 @@
   <dynamicField name="*" type="string" indexed="true" stored="true"/>
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml
index aab5e81..7b8b690 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml
@@ -22,7 +22,7 @@
   <dynamicField name="*" type="string" indexed="true" stored="true"/>
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml b/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml
index aab5e81..7b8b690 100644
--- a/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml
@@ -22,7 +22,7 @@
   <dynamicField name="*" type="string" indexed="true" stored="true"/>
   <!-- for versioning -->
   <field name="_version_" type="long" indexed="true" stored="true"/>
-  <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
   <field name="id" type="string" indexed="true" stored="true"/>
   <uniqueKey>id</uniqueKey>
 </schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
index 02ae888..b1747fd 100644
--- a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
+++ b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
@@ -341,6 +341,59 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
     );
   }
 
+  @Test
+  public void testClientErrorOnMalformedDate() throws Exception {
+    final String BAD_VALUE = "NOT_A_DATE";
+    ignoreException(BAD_VALUE);
+
+    final List<String> FIELDS = new LinkedList<>();
+    for (String type : new String[] {
+        "tdt", "tdt1", "tdtdv", "tdtdv1",
+        "dt_dv", "dt_dvo", "dt", "dt1", "dt_os"
+        }) {
+      FIELDS.add("malformed_" + type);
+    }
+
+    // test that malformed numerics cause client error not server error
+    for (String field : FIELDS) {
+      try {
+        h.update(add( doc("id","100", field, BAD_VALUE)));
+        fail("Didn't encounter an error trying to add a bad date: " + field);
+      } catch (SolrException e) {
+        String msg = e.toString();
+        assertTrue("not an (update) client error on field: " + field +" : "+ msg,
+                   400 <= e.code() && e.code() < 500);
+        assertTrue("(update) client error does not mention bad value: " + msg,
+                   msg.contains(BAD_VALUE));
+        assertTrue("client error does not mention document id: " + msg,
+                   msg.contains("[doc=100]"));
+      }
+      SchemaField sf = h.getCore().getLatestSchema().getField(field); 
+      if (!sf.hasDocValues() && !sf.indexed()) {
+        continue;
+      }
+      try {
+        h.query(req("q",field + ":" + BAD_VALUE));
+        fail("Didn't encounter an error trying to query a bad date: " + field);
+      } catch (SolrException e) {
+        String msg = e.toString();
+        assertTrue("not a (search) client error on field: " + field +" : "+ msg,
+                   400 <= e.code() && e.code() < 500);
+        assertTrue("(search) client error does not mention bad value: " + msg,
+                   msg.contains(BAD_VALUE));
+      }
+      try {
+        h.query(req("q",field + ":[NOW TO " + BAD_VALUE + "]"));
+        fail("Didn't encounter an error trying to query a bad date: " + field);
+      } catch (SolrException e) {
+        String msg = e.toString();
+        assertTrue("not a (search) client error on field: " + field +" : "+ msg,
+                   400 <= e.code() && e.code() < 500);
+        assertTrue("(search) client error does not mention bad value: " + msg,
+                   msg.contains(BAD_VALUE));
+      }
+    }
+  }
 
   @Test
   public void testClientErrorOnMalformedNumbers() throws Exception {
@@ -349,7 +402,13 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
     ignoreException(BAD_VALUE);
 
     final List<String> FIELDS = new LinkedList<>();
-    for (String type : new String[] { "ti", "tf", "td", "tl" }) {
+    for (String type : new String[] {
+        "ti", "tf", "td", "tl",
+        "i", "f", "d", "l",
+        "i_dv", "f_dv", "d_dv", "l_dv",
+        "i_dvo", "f_dvo", "d_dvo", "l_dvo",
+        "i_os", "f_os", "d_os", "l_os"
+        }) {
       FIELDS.add("malformed_" + type);
     }
 
@@ -364,6 +423,12 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
                    400 <= e.code() && e.code() < 500);
         assertTrue("(update) client error does not mention bad value: " + msg,
                    msg.contains(BAD_VALUE));
+        assertTrue("client error does not mention document id",
+                   msg.contains("[doc=100]"));
+      }
+      SchemaField sf = h.getCore().getLatestSchema().getField(field); 
+      if (!sf.hasDocValues() && !sf.indexed()) {
+        continue;
       }
       try {
         h.query(req("q",field + ":" + BAD_VALUE));
@@ -375,6 +440,16 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
         assertTrue("(search) client error does not mention bad value: " + msg,
                    msg.contains(BAD_VALUE));
       }
+      try {
+        h.query(req("q",field + ":[10 TO " + BAD_VALUE + "]"));
+        fail("Didn't encounter an error trying to query a non-number: " + field);
+      } catch (SolrException e) {
+        String msg = e.toString();
+        assertTrue("not a (search) client error on field: " + field +" : "+ msg,
+                   400 <= e.code() && e.code() < 500);
+        assertTrue("(search) client error does not mention bad value: " + msg,
+                   msg.contains(BAD_VALUE));
+      }
     }
   }
   

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java b/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java
index 5545d95..f635436 100644
--- a/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java
@@ -101,6 +101,21 @@ public class BadIndexSchemaTest extends AbstractBadConfigTestBase {
   public void testDocValuesUnsupported() throws Exception {
     doTest("bad-schema-unsupported-docValues.xml", "does not support doc values");
   }
+  
+  public void testRootTypeMissmatchWithUniqueKey() throws Exception {
+    doTest("bad-schema-uniquekey-diff-type-root.xml",
+           "using the exact same fieldType as the uniqueKey field (id) uses: string1");
+  }
+  
+  public void testRootTypeDynamicMissmatchWithUniqueKey() throws Exception {
+    // in this case, the core should load fine -- but we should get an error adding docs
+    try {
+      initCore("solrconfig.xml","bad-schema-uniquekey-diff-type-dynamic-root.xml");
+      assertFailedU("Unable to index docs with children", adoc(sdocWithChildren("1","-1")));
+    } finally {
+      deleteCore();
+    }
+  }
 
   public void testSweetSpotSimBadConfig() throws Exception {
     doTest("bad-schema-sweetspot-both-tf.xml", "Can not mix");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java b/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
index 3f57ee5..f282c3f 100644
--- a/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
+++ b/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
@@ -20,6 +20,7 @@ import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.util.AbstractSolrTestCase;
 import org.junit.BeforeClass;
+import org.junit.Test;
 
 public class TestQueryTypes extends AbstractSolrTestCase {
 
@@ -209,7 +210,6 @@ public class TestQueryTypes extends AbstractSolrTestCase {
            ,"//result[@numFound='2']"
            );
 
-
     //
     // test escapes in quoted strings
     //
@@ -436,5 +436,49 @@ public class TestQueryTypes extends AbstractSolrTestCase {
     assertQ("Test text field with no analysis doesn't NPE with wildcards (SOLR-4318)",
         req("q", "text_no_analyzer:should*"), "//result[@numFound='1']");
 
+    
+  }
+  
+  @Test
+  public void testNumericBadRequests() {
+    String[] suffixes = new String[50];
+    int fieldNum = 0;
+    for (String type:new String[]{"i", "l", "f", "d", "dt"}) {
+      for (String s:new String[]{"", "s"}) {
+        //Trie
+        suffixes[fieldNum++] = "t" + type + s;
+        suffixes[fieldNum++] = "t" + type + s + "_dv";
+        suffixes[fieldNum++] = "t" + type + s + "_ni_dv";
+        
+        //Points
+        suffixes[fieldNum++] = type + s + "_p";
+        suffixes[fieldNum++] = type + s + "_ni_p";
+      }
+    }
+    assertEquals(fieldNum,suffixes.length);
+    
+    String badNumber = "NOT_A_NUMBER";
+    for (String suffix:suffixes) {
+      // Numeric bad requests
+      assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!term f=foo_" + suffix + "}" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+      assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!terms f=foo_" + suffix + "}1 2 3 4 5 " + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+      assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!lucene}foo_" + suffix + ":" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+      assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!field f=foo_" + suffix + "}" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+      assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!maxscore}foo_" + suffix + ":" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+      assertQEx("Expecting exception for suffix: " + suffix, badNumber,
+          req("q","{!xmlparser}<PointRangeQuery fieldName=\"foo_"+ suffix  + "\" lowerTerm=\"1\" upperTerm=\"" + badNumber + "\"/>"), SolrException.ErrorCode.BAD_REQUEST);
+      if (suffix.contains("_p")) {
+        // prefix queries work in Trie fields
+        assertQEx("Expecting exception for suffix: " + suffix, "Can't run prefix queries on numeric fields",
+            req("q","{!prefix f=foo_" + suffix + "}NOT_A_NUMBER"), SolrException.ErrorCode.BAD_REQUEST);
+        assertQEx("Expecting exception for suffix: " + suffix, "Can't run prefix queries on numeric fields",
+            req("q","{!lucene}foo_" + suffix + ":123*"), SolrException.ErrorCode.BAD_REQUEST);
+      }
+      
+      // Skipping: func, boost, raw, nested, frange, spatial*, join, surround, switch, parent, child, collapsing, 
+      // complexphrase, rerank, export, mlt, hash, graph, graphTerms, igain, tlogit, sigificantTerms, payload*
+      // Maybe add: raw, join, parent, child, collapsing, graphTerms, igain, sigificantTerms, simple
+    }
+
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6396cb75/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
index 1a2e572..4b82e62 100644
--- a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
+++ b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
@@ -33,6 +33,7 @@ import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TermInSetQuery;
 import org.apache.lucene.search.TermQuery;
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.metrics.MetricsMap;
 import org.apache.solr.common.params.ModifiableSolrParams;
@@ -1038,4 +1039,39 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 {
         , "/response/numFound==1"
     );
   }
+  
+  @Test
+  public void testBadRequestInSetQuery() throws SyntaxError {
+    SolrQueryRequest req = req();
+    QParser qParser;
+    String[] fieldSuffix = new String[] {
+        "ti", "tf", "td", "tl",
+        "i", "f", "d", "l",
+        "is", "fs", "ds", "ls",
+        "i_dv", "f_dv", "d_dv", "l_dv",
+        "is_dv", "fs_dv", "ds_dv", "ls_dv",
+        "i_dvo", "f_dvo", "d_dvo", "l_dvo",
+    };
+    
+    for (String suffix:fieldSuffix) {
+      //Good queries
+      qParser = QParser.getParser("foo_" + suffix + ":(1 2 3 4 5 6 7 8 9 10 20 19 18 17 16 15 14 13 12 25)", req);
+      qParser.setIsFilter(true);
+      qParser.getQuery();
+    }
+    
+    for (String suffix:fieldSuffix) {
+      qParser = QParser.getParser("foo_" + suffix + ":(1 2 3 4 5 6 7 8 9 10 20 19 18 17 16 15 14 13 12 NOT_A_NUMBER)", req);
+      qParser.setIsFilter(true); // this may change in the future
+      try {
+        qParser.getQuery();
+        fail("Expecting exception");
+      } catch (SolrException e) {
+        assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+        assertTrue("Unexpected exception: " + e.getMessage(), e.getMessage().contains("Invalid Number: NOT_A_NUMBER"));
+      }
+    }
+    
+    
+  }
 }
\ No newline at end of file


Mime
View raw message