Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement filter pushdown #3482

Merged
merged 26 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 68 additions & 4 deletions internal/backends/postgresql/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"github.com/FerretDB/FerretDB/internal/backends/postgresql/metadata"
"github.com/FerretDB/FerretDB/internal/backends/postgresql/metadata/pool"
"github.com/FerretDB/FerretDB/internal/handlers/sjson"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/util/must"
)
Expand Down Expand Up @@ -55,6 +56,10 @@
return nil, lazyerrors.Error(err)
}

if params == nil {
params = new(backends.QueryParams)
}

Check warning on line 61 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L59-L61

Added lines #L59 - L61 were not covered by tests

if p == nil {
return &backends.QueryResult{
Iter: newQueryIterator(ctx, nil),
Expand All @@ -72,14 +77,22 @@
}, nil
}

// TODO https://github.com/FerretDB/FerretDB/issues/3414
q := fmt.Sprintf(
`SELECT %s FROM %s`,
AlekSi marked this conversation as resolved.
Show resolved Hide resolved
metadata.DefaultColumn,
pgx.Identifier{c.dbName, meta.TableName}.Sanitize(),
)

rows, err := p.Query(ctx, q)
var placeholder Placeholder

where, args, err := prepareWhereClause(&placeholder, params.Filter)
if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 91 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L86-L91

Added lines #L86 - L91 were not covered by tests

q += where

rows, err := p.Query(ctx, q, args...)

Check warning on line 95 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L93-L95

Added lines #L93 - L95 were not covered by tests
if err != nil {
return nil, lazyerrors.Error(err)
}
Expand Down Expand Up @@ -245,8 +258,59 @@

// Explain implements backends.Collection interface.
func (c *collection) Explain(ctx context.Context, params *backends.ExplainParams) (*backends.ExplainResult, error) {
// TODO https://github.com/FerretDB/FerretDB/issues/3389
return new(backends.ExplainResult), nil
p, err := c.r.DatabaseGetExisting(ctx, c.dbName)
if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 264 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L261-L264

Added lines #L261 - L264 were not covered by tests

res := new(backends.ExplainResult)

if p == nil {
return res, nil
}

Check warning on line 270 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L266-L270

Added lines #L266 - L270 were not covered by tests

meta, err := c.r.CollectionGet(ctx, c.dbName, c.name)
if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 275 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L272-L275

Added lines #L272 - L275 were not covered by tests

if meta == nil {
res.QueryPlanner = must.NotFail(types.NewDocument())
return res, nil
}

Check warning on line 280 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L277-L280

Added lines #L277 - L280 were not covered by tests

q := fmt.Sprintf(
`EXPLAIN (VERBOSE true, FORMAT JSON) SELECT %s FROM %s`,
AlekSi marked this conversation as resolved.
Show resolved Hide resolved
metadata.DefaultColumn,
pgx.Identifier{c.dbName, meta.TableName}.Sanitize(),
)

var placeholder Placeholder

where, args, err := prepareWhereClause(&placeholder, params.Filter)
if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 293 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L282-L293

Added lines #L282 - L293 were not covered by tests

res.QueryPushdown = where != ""

q += where

var b []byte
err = p.QueryRow(ctx, q, args...).Scan(&b)

if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 304 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L295-L304

Added lines #L295 - L304 were not covered by tests

queryPlan, err := unmarshalExplain(b)
if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 309 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L306-L309

Added lines #L306 - L309 were not covered by tests

res.QueryPlanner = queryPlan

return res, nil

Check warning on line 313 in internal/backends/postgresql/collection.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/collection.go#L311-L313

Added lines #L311 - L313 were not covered by tests
}

// Stats implements backends.Collection interface.
Expand Down
74 changes: 74 additions & 0 deletions internal/backends/postgresql/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2021 FerretDB Inc.
//
// Licensed 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.

package postgresql

import (
"encoding/json"
"errors"
"fmt"

"golang.org/x/exp/maps"

"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
)

// unmarshalExplain unmarshalls the plan from EXPLAIN postgreSQL command.
// EXPLAIN result is not sjson, so it cannot be unmarshalled by sjson.Unmarshal.
func unmarshalExplain(b []byte) (*types.Document, error) {
var plans []map[string]any
if err := json.Unmarshal(b, &plans); err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 34 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L30-L34

Added lines #L30 - L34 were not covered by tests

if len(plans) == 0 {
return nil, lazyerrors.Error(errors.New("no execution plan returned"))
}

Check warning on line 38 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L36-L38

Added lines #L36 - L38 were not covered by tests

return convertJSON(plans[0]).(*types.Document), nil

Check warning on line 40 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L40

Added line #L40 was not covered by tests
}

// convertJSON transforms decoded JSON map[string]any value into *types.Document.
func convertJSON(value any) any {
switch value := value.(type) {
case map[string]any:
d := types.MakeDocument(len(value))
keys := maps.Keys(value)

for _, k := range keys {
v := value[k]
d.Set(k, convertJSON(v))
}

Check warning on line 53 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L44-L53

Added lines #L44 - L53 were not covered by tests

return d

Check warning on line 55 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L55

Added line #L55 was not covered by tests

case []any:
a := types.MakeArray(len(value))
for _, v := range value {
a.Append(convertJSON(v))
}

Check warning on line 61 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L57-L61

Added lines #L57 - L61 were not covered by tests

return a

Check warning on line 63 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L63

Added line #L63 was not covered by tests

case nil:
return types.Null

Check warning on line 66 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L65-L66

Added lines #L65 - L66 were not covered by tests

case float64, string, bool:
return value

Check warning on line 69 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L68-L69

Added lines #L68 - L69 were not covered by tests

default:
panic(fmt.Sprintf("unsupported type: %[1]T (%[1]v)", value))

Check warning on line 72 in internal/backends/postgresql/helpers.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/postgresql/helpers.go#L71-L72

Added lines #L71 - L72 were not covered by tests
}
}
Loading
Loading