diff --git a/api.go b/api.go index 458164f6e..42d7e3b0d 100644 --- a/api.go +++ b/api.go @@ -1688,6 +1688,15 @@ func (api *API) ImportValueWithTx(ctx context.Context, qcx *Qcx, req *ImportValu return errors.Wrap(err, "validating api method") } + numCols := len(req.ColumnIDs) + len(req.ColumnKeys) + numVals := len(req.Values) + len(req.FloatValues) + len(req.TimestampValues) + len(req.StringValues) + if numCols != numVals { + return errors.New(fmt.Sprintf("number of columns (%v) and number of values (%v) do not match", numCols, numVals)) + } + if numCols == 0 { + return nil + } + idx, field, err := api.indexField(req.Index, req.Field, req.Shard) if err != nil { return errors.Wrap(err, fmt.Sprintf("getting index '%v' and field '%v'; shard=%v", req.Index, req.Field, req.Shard)) @@ -1798,7 +1807,6 @@ func (api *API) ImportValueWithTx(ctx context.Context, qcx *Qcx, req *ImportValu return errors.Wrap(err, "importing value") } // end if req.Shard != math.MaxUint64 - options.IgnoreKeyCheck = true start := 0 shard := req.ColumnIDs[0] / ShardWidth diff --git a/api_test.go b/api_test.go index 378aee9fd..08e1b49e5 100644 --- a/api_test.go +++ b/api_test.go @@ -251,6 +251,60 @@ func TestAPI_ImportValue(t *testing.T) { } }) + t.Run("ValIntEmpty", func(t *testing.T) { + ctx := context.Background() + index := "valintempty" + field := "fld" + createIndexForTest(index, coord, t) + createFieldForTest(index, field, coord, t) + + // Column keys are sharded so their order is not guaranteed. + colKeys := []string{"col2", "col1", "col3"} + values := []int64{1, 2, 3, 4} + + // Import without data, verify that it succeeds + req := &pilosa.ImportValueRequest{ + Index: index, + Field: field, + } + qcx1 := coord.API.Txf().NewQcx() + defer qcx1.Abort() + + // Import with empty request, should succeed + if err := coord.API.ImportValue(ctx, qcx1, req); err != nil { + t.Fatal(err) + } + PanicOn(qcx1.Finish()) + + // Import without data but with columnkeys, verify that it errors + req.ColumnKeys = colKeys + qcx2 := coord.API.Txf().NewQcx() + defer qcx2.Abort() + if err := coord.API.ImportValue(ctx, qcx2, req); err == nil { + t.Fatal("expected error but succeeded") + } + PanicOn(qcx2.Finish()) + + // Import with mismatch column and value lengths + req.Values = values + qcx3 := coord.API.Txf().NewQcx() + defer qcx3.Abort() + if err := coord.API.ImportValue(ctx, qcx3, req); err == nil { + t.Fatal("expected error but succeeded") + } + PanicOn(qcx3.Finish()) + + // Import with data but no columns + req.ColumnKeys = make([]string, 0) + qcx4 := coord.API.Txf().NewQcx() + defer qcx4.Abort() + if err := coord.API.ImportValue(ctx, qcx4, req); err == nil { + t.Fatal("expected error but succeeded") + } + PanicOn(qcx4.Finish()) + + }) + t.Run("ValDecimalField", func(t *testing.T) { ctx := context.Background() index := "valdec" @@ -1225,3 +1279,19 @@ func TestAPI_MutexCheck(t *testing.T) { }) } } + +func createIndexForTest(index string, coord *test.Command, t *testing.T) { + ctx := context.Background() + _, err := coord.API.CreateIndex(ctx, index, pilosa.IndexOptions{Keys: true}) + if err != nil { + t.Fatalf("creating index: %v", err) + } +} + +func createFieldForTest(index string, field string, coord *test.Command, t *testing.T) { + ctx := context.Background() + _, err := coord.API.CreateField(ctx, index, field, pilosa.OptFieldTypeInt(math.MinInt64, math.MaxInt64)) + if err != nil { + t.Fatalf("creating field: %v", err) + } +}