You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dw...@apache.org on 2021/03/10 09:58:30 UTC
[lucene] 15/49: SOLR-10860: Return proper error code for bad input
incase of inplace updates (#2121)
This is an automated email from the ASF dual-hosted git repository.
dweiss pushed a commit to branch jira/solr-13105-toMerge
in repository https://gitbox.apache.org/repos/asf/lucene.git
commit e91d0e95b509f47f3ca5b6422fc18208b1f9c636
Author: S N Munendra <mu...@apache.org>
AuthorDate: Thu Jan 7 20:44:48 2021 +0530
SOLR-10860: Return proper error code for bad input incase of inplace updates (#2121)
Return proper error code on invalid value with in-place update.
Handle invalid value for inc op with the in-place update, uses toNativeType to convert increment value instead of direct parsing. Also, return an error when inc operation is specified for the non-numeric field
---
solr/CHANGES.txt | 2 +
.../processor/AtomicUpdateDocumentMerger.java | 79 +++++++++++++++-------
.../solr/update/TestInPlaceUpdatesDistrib.java | 18 ++++-
.../solr/update/TestInPlaceUpdatesStandalone.java | 32 +++++++++
.../solr/update/processor/AtomicUpdatesTest.java | 13 ++++
5 files changed, 118 insertions(+), 26 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index adc0820..b4551c5 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -296,6 +296,8 @@ Bug Fixes
* SOLR-15047: Fix collapse parser behavior when collapsing on numeric fields to differentiate '0' group from null group (hossman)
+ * SOLR-10860: Return proper error code for bad input in case of inplace updates (Tomas Eduardo Fernandez Lobbe, Munendra S N)
+
Other Changes
---------------------
diff --git a/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java b/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
index bdc4a72..1dd993a 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
@@ -16,6 +16,24 @@
*/
package org.apache.solr.update.processor;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
import org.apache.commons.lang3.tuple.Pair;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.util.BytesRef;
@@ -43,15 +61,6 @@ import org.apache.solr.util.RefCounted;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.function.BiConsumer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Stream;
-
import static org.apache.solr.common.params.CommonParams.ID;
/**
@@ -120,13 +129,8 @@ public class AtomicUpdateDocumentMerger {
doAddDistinct(toDoc, sif, fieldVal);
break;
default:
- Object id = toDoc.containsKey(idField.getName())? toDoc.getFieldValue(idField.getName()):
- fromDoc.getFieldValue(idField.getName());
- String err = "Unknown operation for the an atomic update, operation ignored: " + key;
- if (id != null) {
- err = err + " for id:" + id;
- }
- throw new SolrException(ErrorCode.BAD_REQUEST, err);
+ throw new SolrException(ErrorCode.BAD_REQUEST,
+ "Error:" + getID(toDoc, schema) + " Unknown operation for the an atomic update: " + key);
}
// validate that the field being modified is not the id field.
if (idField.getName().equals(sif.getName())) {
@@ -143,6 +147,15 @@ public class AtomicUpdateDocumentMerger {
return toDoc;
}
+ private static String getID(SolrInputDocument doc, IndexSchema schema) {
+ String id = "";
+ SchemaField sf = schema.getUniqueKeyField();
+ if (sf != null) {
+ id = "[doc="+doc.getFieldValue( sf.getName() )+"] ";
+ }
+ return id;
+ }
+
/**
* Given a schema field, return whether or not such a field is supported for an in-place update.
* Note: If an update command has updates to only supported fields (and _version_ is also supported),
@@ -470,6 +483,11 @@ public class AtomicUpdateDocumentMerger {
protected void doInc(SolrInputDocument toDoc, SolrInputField sif, Object fieldVal) {
SolrInputField numericField = toDoc.get(sif.getName());
SchemaField sf = schema.getField(sif.getName());
+
+ if (sf.getType().getNumberType() == null) {
+ throw new SolrException(ErrorCode.BAD_REQUEST, "'inc' is not supported on non-numeric field " + sf.getName());
+ }
+
if (numericField != null || sf.getDefaultValue() != null) {
// TODO: fieldtype needs externalToObject?
String oldValS = (numericField != null) ?
@@ -478,20 +496,23 @@ public class AtomicUpdateDocumentMerger {
sf.getType().readableToIndexed(oldValS, term);
Object oldVal = sf.getType().toObject(sf, term.get());
- String fieldValS = fieldVal.toString();
- Number result;
+ // behavior similar to doAdd/doSet
+ Object resObj = getNativeFieldValue(sf.getName(), fieldVal);
+ if (!(resObj instanceof Number)) {
+ throw new SolrException(ErrorCode.BAD_REQUEST, "Invalid input '" + resObj + "' for field " + sf.getName());
+ }
+ Number result = (Number)resObj;
if (oldVal instanceof Long) {
- result = ((Long) oldVal).longValue() + Long.parseLong(fieldValS);
+ result = ((Long) oldVal).longValue() + result.longValue();
} else if (oldVal instanceof Float) {
- result = ((Float) oldVal).floatValue() + Float.parseFloat(fieldValS);
+ result = ((Float) oldVal).floatValue() + result.floatValue();
} else if (oldVal instanceof Double) {
- result = ((Double) oldVal).doubleValue() + Double.parseDouble(fieldValS);
+ result = ((Double) oldVal).doubleValue() + result.doubleValue();
} else {
// int, short, byte
- result = ((Integer) oldVal).intValue() + Integer.parseInt(fieldValS);
+ result = ((Integer) oldVal).intValue() + result.intValue();
}
-
- toDoc.setField(sif.getName(), result);
+ toDoc.setField(sif.getName(), result);
} else {
toDoc.setField(sif.getName(), fieldVal);
}
@@ -553,7 +574,15 @@ public class AtomicUpdateDocumentMerger {
return val;
}
SchemaField sf = schema.getField(fieldName);
- return sf.getType().toNativeType(val);
+ try {
+ return sf.getType().toNativeType(val);
+ } catch (SolrException ex) {
+ throw new SolrException(SolrException.ErrorCode.getErrorCode(ex.code()),
+ "Error converting field '" + sf.getName() + "'='" +val+"' to native type, msg=" + ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
+ "Error converting field '" + sf.getName() + "'='" +val+"' to native type, msg=" + ex.getMessage(), ex);
+ }
}
private static boolean isChildDoc(Object obj) {
diff --git a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
index baf6320..3eccdfa 100644
--- a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
+++ b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
@@ -47,6 +47,7 @@ import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
import org.apache.solr.cloud.ZkShardTerms;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
@@ -56,18 +57,21 @@ import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.TimeSource;
import org.apache.solr.index.NoMergePolicyFactory;
import org.apache.solr.update.processor.DistributedUpdateProcessor;
-import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.util.RefCounted;
import org.apache.solr.util.TimeOut;
import org.apache.zookeeper.KeeperException;
+import org.hamcrest.MatcherAssert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.hamcrest.core.StringContains.containsString;
+
/**
* Tests the in-place updates (docValues updates) for a one shard, three replica cluster.
*/
@@ -682,6 +686,18 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
assertTrue(currentVersion > version);
version = currentVersion;
+ // set operation with invalid value for field
+ SolrException e = expectThrows(SolrException.class,
+ () -> addDocAndGetVersion( "id", 100, "inplace_updatable_float", map("set", "NOT_NUMBER")));
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ MatcherAssert.assertThat(e.getMessage(), containsString("For input string: \"NOT_NUMBER\""));
+
+ // inc operation with invalid inc value
+ e = expectThrows(SolrException.class,
+ () -> addDocAndGetVersion( "id", 100, "inplace_updatable_int", map("inc", "NOT_NUMBER")));
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ MatcherAssert.assertThat(e.getMessage(), containsString("For input string: \"NOT_NUMBER\""));
+
// RTG from tlog(s)
for (SolrClient client : clients) {
final String clientDebug = client.toString() + (LEADER.equals(client) ? " (leader)" : " (not leader)");
diff --git a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java
index 2867935..91586ca 100644
--- a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java
+++ b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java
@@ -18,6 +18,7 @@
package org.apache.solr.update;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -48,6 +49,7 @@ import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.update.processor.AtomicUpdateDocumentMerger;
import org.apache.solr.util.RefCounted;
+import org.hamcrest.MatcherAssert;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -122,6 +124,36 @@ public class TestInPlaceUpdatesStandalone extends SolrTestCaseJ4 {
}
@Test
+ public void testUpdateBadRequest() throws Exception {
+ final long version1 = addAndGetVersion(sdoc("id", "1", "title_s", "first", "inplace_updatable_float", 41), null);
+ assertU(commit());
+
+ // invalid value with set operation
+ SolrException e = expectThrows(SolrException.class,
+ () -> addAndAssertVersion(version1, "id", "1", "inplace_updatable_float", map("set", "NOT_NUMBER")));
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ MatcherAssert.assertThat(e.getMessage(), containsString("For input string: \"NOT_NUMBER\""));
+
+ // invalid value with inc operation
+ e = expectThrows(SolrException.class,
+ () -> addAndAssertVersion(version1, "id", "1", "inplace_updatable_float", map("inc", "NOT_NUMBER")));
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ MatcherAssert.assertThat(e.getMessage(), containsString("For input string: \"NOT_NUMBER\""));
+
+ // inc op with null value
+ e = expectThrows(SolrException.class,
+ () -> addAndAssertVersion(version1, "id", "1", "inplace_updatable_float", map("inc", null)));
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ MatcherAssert.assertThat(e.getMessage(), containsString("Invalid input 'null' for field inplace_updatable_float"));
+
+ e = expectThrows(SolrException.class,
+ () -> addAndAssertVersion(version1, "id", "1", "inplace_updatable_float",
+ map("inc", new ArrayList<>(Collections.singletonList(123)))));
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ MatcherAssert.assertThat(e.getMessage(), containsString("Invalid input '[123]' for field inplace_updatable_float"));
+ }
+
+ @Test
public void testUpdatingDocValues() throws Exception {
long version1 = addAndGetVersion(sdoc("id", "1", "title_s", "first", "inplace_updatable_float", 41), null);
long version2 = addAndGetVersion(sdoc("id", "2", "title_s", "second", "inplace_updatable_float", 42), null);
diff --git a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
index 54e9f9d..a0c1402 100644
--- a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
+++ b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
@@ -24,13 +24,17 @@ import java.util.List;
import com.google.common.collect.ImmutableMap;
import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.util.DateMathParser;
+import org.hamcrest.MatcherAssert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
+import static org.hamcrest.core.StringContains.containsString;
+
public class AtomicUpdatesTest extends SolrTestCaseJ4 {
@BeforeClass
@@ -1420,6 +1424,15 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 {
assertQ(req("q", "id:123", "indent", "true"), "//result[@numFound = '1']");
assertQ(req("q", "id:12311", "indent", "true"), "//result[@numFound = '1']");
assertQ(req("q", "cat:ccc", "indent", "true"), "//result[@numFound = '1']");
+
+ // inc op on non-numeric field
+ SolrInputDocument invalidDoc = new SolrInputDocument();
+ invalidDoc.setField("id", "7");
+ invalidDoc.setField("cat", ImmutableMap.of("inc", "bbb"));
+
+ SolrException e = expectThrows(SolrException.class, () -> assertU(adoc(invalidDoc)));
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ MatcherAssert.assertThat(e.getMessage(), containsString("'inc' is not supported on non-numeric field cat"));
}
public void testFieldsWithDefaultValuesWhenAtomicUpdatesAgainstTlog() {