diff --git a/integration/basic_test.go b/integration/basic_test.go index 9a2fcfbab137..abbdc5a48a84 100644 --- a/integration/basic_test.go +++ b/integration/basic_test.go @@ -232,62 +232,6 @@ func TestEmptyKey(t *testing.T) { assert.Equal(t, expected, actual) } -func TestFindAndModifyCommentMethod(t *testing.T) { - t.Parallel() - ctx, collection := setup.Setup(t, shareddata.Scalars) - - name := collection.Database().Name() - databaseNames, err := collection.Database().Client().ListDatabaseNames(ctx, bson.D{}) - require.NoError(t, err) - - comment := "*/ 1; DROP SCHEMA " + name + " CASCADE -- " - filter := bson.D{{"_id", "string"}} - - opts := options.Delete().SetComment(comment) - res, err := collection.DeleteOne(ctx, filter, opts) - require.NoError(t, err) - - expected := &mongo.DeleteResult{ - DeletedCount: 1, - } - - assert.Contains(t, databaseNames, name) - assert.Equal(t, expected, res) -} - -func TestFindAndModifyCommentQuery(t *testing.T) { - t.Parallel() - ctx, collection := setup.Setup(t, shareddata.Scalars) - - name := collection.Database().Name() - databaseNames, err := collection.Database().Client().ListDatabaseNames(ctx, bson.D{}) - require.NoError(t, err) - - comment := "*/ 1; DROP SCHEMA " + name + " CASCADE -- " - request := bson.D{ - {"findAndModify", collection.Name()}, - {"query", bson.D{{"_id", "string"}, {"$comment", comment}}}, - {"update", bson.D{{"$set", bson.D{{"v", "bar"}}}}}, - } - - expectedLastErrObj := bson.D{ - {"n", int32(1)}, - {"updatedExisting", true}, - } - - var actual bson.D - err = collection.Database().RunCommand(ctx, request).Decode(&actual) - require.NoError(t, err) - - lastErrObj, ok := actual.Map()["lastErrorObject"].(bson.D) - if !ok { - t.Fatal(actual) - } - - assert.Contains(t, databaseNames, name) - AssertEqualDocuments(t, expectedLastErrObj, lastErrObj) -} - func TestCollectionName(t *testing.T) { t.Parallel() diff --git a/integration/create_test.go b/integration/create_test.go index fd197786d5f2..35f3ec5ec1bc 100644 --- a/integration/create_test.go +++ b/integration/create_test.go @@ -92,9 +92,11 @@ func TestCreateStress(t *testing.T) { } } -func TestCreateOnInsertStressSameCollection(t *testing.T) { +func TestCreateOnInsertStressSameCollection(tt *testing.T) { // TODO rewrite using teststress.Stress + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/2747") + ctx, collection := setup.Setup(t) // do not toLower() db name as it may contain uppercase letters db := collection.Database().Client().Database(t.Name()) @@ -173,9 +175,11 @@ func TestCreateOnInsertStressDiffCollection(t *testing.T) { wg.Wait() } -func TestCreateStressSameCollection(t *testing.T) { +func TestCreateStressSameCollection(tt *testing.T) { // TODO rewrite using teststress.Stress + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/2747") + ctx, collection := setup.Setup(t) // no providers there, we will create collection from the test db := collection.Database() @@ -234,14 +238,10 @@ func TestCreateStressSameCollection(t *testing.T) { require.Len(t, colls, 1) // check that the collection was created, and we can query it - t.Run("check_stress", func(t *testing.T) { - t.Parallel() - - var doc bson.D - err := db.Collection(collName).FindOne(ctx, bson.D{{"_id", "foo_1"}}).Decode(&doc) - require.NoError(t, err) - require.Equal(t, bson.D{{"_id", "foo_1"}, {"v", "bar"}}, doc) - }) + var doc bson.D + err = db.Collection(collName).FindOne(ctx, bson.D{{"_id", "foo_1"}}).Decode(&doc) + require.NoError(t, err) + require.Equal(t, bson.D{{"_id", "foo_1"}, {"v", "bar"}}, doc) require.Equal(t, int32(1), created.Load(), "Only one attempt to create a collection should succeed") } diff --git a/integration/explain_compat_test.go b/integration/explain_compat_test.go index c6673c7b5e41..1d1f695cec74 100644 --- a/integration/explain_compat_test.go +++ b/integration/explain_compat_test.go @@ -110,7 +110,6 @@ func testExplainCompatError(t *testing.T, testCases map[string]explainCompatTest assert.Equal(t, compatMap["ok"], targetMap["ok"]) assert.Equal(t, compatMap["command"], targetMap["command"]) - // check queryPlanner is set assert.NotEmpty(t, targetMap["queryPlanner"]) var nonEmptyResults bool diff --git a/integration/findandmodify_test.go b/integration/findandmodify_test.go index f8070f38022a..4759caae9d82 100644 --- a/integration/findandmodify_test.go +++ b/integration/findandmodify_test.go @@ -22,47 +22,52 @@ import ( "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "github.com/FerretDB/FerretDB/integration/setup" "github.com/FerretDB/FerretDB/integration/shareddata" + "github.com/FerretDB/FerretDB/internal/types" + "github.com/FerretDB/FerretDB/internal/util/must" + "github.com/FerretDB/FerretDB/internal/util/testutil" ) -func TestFindAndModifyEmptyCollectionName(t *testing.T) { - t.Parallel() +func TestFindAndModifyEmptyCollectionName(tt *testing.T) { + tt.Parallel() - for name, tc := range map[string]struct { - err *mongo.CommandError // optional, expected error from MongoDB - altMessage string // optional, alternative error message for FerretDB, ignored if empty - skip string // optional, skip test with a specified reason - }{ - "EmptyCollectionName": { - err: &mongo.CommandError{ - Code: 73, - Message: "Invalid namespace specified 'TestFindAndModifyEmptyCollectionName-EmptyCollectionName.'", - Name: "InvalidNamespace", - }, - altMessage: "Invalid namespace specified 'TestFindAndModifyEmptyCollectionName-EmptyCollectionName.'", + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/3049") + + ctx, collection := setup.Setup(t, shareddata.Doubles) + + var res bson.D + err := collection.Database().RunCommand(ctx, bson.D{{"findAndModify", ""}}).Decode(&res) + + assert.Nil(t, res) + + AssertEqualCommandError( + t, + mongo.CommandError{ + Code: 73, + Message: "Invalid namespace specified 'TestFindAndModifyEmptyCollectionName.'", + Name: "InvalidNamespace", }, - } { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - if tc.skip != "" { - t.Skip(tc.skip) - } + err, + ) +} - t.Parallel() +func TestFindAndModifyNonExistingCollection(tt *testing.T) { + tt.Parallel() - require.NotNil(t, tc.err, "err must not be nil") + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/3049") - ctx, collection := setup.Setup(t, shareddata.Doubles) + ctx, collection := setup.Setup(t) - var res bson.D - err := collection.Database().RunCommand(ctx, bson.D{{"findAndModify", ""}}).Decode(&res) + var actual bson.D + err := collection.FindOneAndUpdate( + ctx, bson.D{}, bson.D{{"$set", bson.E{"foo", "bar"}}}, + ).Decode(&actual) - assert.Nil(t, res) - AssertEqualAltCommandError(t, *tc.err, tc.altMessage, err) - }) - } + assert.Equal(t, mongo.ErrNoDocuments, err) + assert.Nil(t, actual) } func TestFindAndModifyCommandErrors(t *testing.T) { @@ -442,12 +447,14 @@ func TestFindAndModifyCommandErrors(t *testing.T) { }, } { name, tc := name, tc - t.Run(name, func(t *testing.T) { + t.Run(name, func(tt *testing.T) { if tc.skip != "" { - t.Skip(tc.skip) + tt.Skip(tc.skip) } - t.Parallel() + tt.Parallel() + + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/3049") require.NotNil(t, tc.command, "command must not be nil") require.NotNil(t, tc.err, "err must not be nil") @@ -547,8 +554,10 @@ func TestFindAndModifyCommandUpsert(t *testing.T) { }, } { name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() + t.Run(name, func(tt *testing.T) { + tt.Parallel() + + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/3049") require.NotNil(t, tc.command, "command must not be nil") @@ -579,15 +588,64 @@ func TestFindAndModifyCommandUpsert(t *testing.T) { } } -func TestFindAndModifyNonExistingCollection(t *testing.T) { - t.Parallel() - ctx, collection := setup.Setup(t) +func TestFindAndModifyCommentMethod(tt *testing.T) { + tt.Parallel() + + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/3049") + + ctx, collection := setup.Setup(t, shareddata.Scalars) + + name := collection.Database().Name() + databaseNames, err := collection.Database().Client().ListDatabaseNames(ctx, bson.D{}) + require.NoError(t, err) + + comment := "*/ 1; DROP SCHEMA " + name + " CASCADE -- " + filter := bson.D{{"_id", "string"}} + + opts := options.Delete().SetComment(comment) + res, err := collection.DeleteOne(ctx, filter, opts) + require.NoError(t, err) + + expected := &mongo.DeleteResult{ + DeletedCount: 1, + } + + assert.Contains(t, databaseNames, name) + assert.Equal(t, expected, res) +} + +func TestFindAndModifyCommentQuery(tt *testing.T) { + tt.Parallel() + + t := setup.FailsForSQLite(tt, "https://github.com/FerretDB/FerretDB/issues/3049") + + ctx, collection := setup.Setup(t, shareddata.Scalars) + + name := collection.Database().Name() + databaseNames, err := collection.Database().Client().ListDatabaseNames(ctx, bson.D{}) + require.NoError(t, err) + + comment := "*/ 1; DROP SCHEMA " + name + " CASCADE -- " + request := bson.D{ + {"findAndModify", collection.Name()}, + {"query", bson.D{{"_id", "string"}, {"$comment", comment}}}, + {"update", bson.D{{"$set", bson.D{{"v", "bar"}}}}}, + } var actual bson.D - err := collection.FindOneAndUpdate( - ctx, bson.D{}, bson.D{{"$set", bson.E{"foo", "bar"}}}, - ).Decode(&actual) + err = collection.Database().RunCommand(ctx, request).Decode(&actual) + require.NoError(t, err) - assert.Equal(t, mongo.ErrNoDocuments, err) - assert.Nil(t, actual) + lastErrObj, _ := ConvertDocument(t, actual).Get("lastErrorObject") + if lastErrObj == nil { + t.Fatal(actual) + } + + assert.Contains(t, databaseNames, name) + + expectedLastErrObj := must.NotFail(types.NewDocument( + "n", int32(1), + "updatedExisting", true, + )) + testutil.AssertEqual(t, expectedLastErrObj, lastErrObj.(*types.Document)) } diff --git a/internal/handlers/pg/msg_explain.go b/internal/handlers/pg/msg_explain.go index 7a786baa596a..a15ef5da4720 100644 --- a/internal/handlers/pg/msg_explain.go +++ b/internal/handlers/pg/msg_explain.go @@ -110,10 +110,13 @@ func (h *Handler) MsgExplain(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, "queryPlanner", queryPlanner, "explainVersion", "1", "command", cmd, + "serverInfo", serverInfo, + + // our extensions "pushdown", results.FilterPushdown, "sortingPushdown", results.SortPushdown, "limitPushdown", results.LimitPushdown, - "serverInfo", serverInfo, + "ok", float64(1), ))}, })) diff --git a/internal/handlers/sqlite/msg_explain.go b/internal/handlers/sqlite/msg_explain.go index 0e3ce9e856de..c99d4d406160 100644 --- a/internal/handlers/sqlite/msg_explain.go +++ b/internal/handlers/sqlite/msg_explain.go @@ -55,13 +55,21 @@ func (h *Handler) MsgExplain(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, cmd := params.Command cmd.Set("$db", params.DB) + queryPlanner := types.MakeDocument(0) + var reply wire.OpMsg must.NoError(reply.SetSections(wire.OpMsgSection{ Documents: []*types.Document{must.NotFail(types.NewDocument( + "queryPlanner", queryPlanner, "explainVersion", "1", "command", cmd, - "pushdown", !h.DisableFilterPushdown, "serverInfo", serverInfo, + + // our extensions + "pushdown", !h.DisableFilterPushdown, + "sortingPushdown", false, + "limitPushdown", false, + "ok", float64(1), ))}, }))