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

Use one implementation for finding path values #3087

Merged
merged 58 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bce806c
create initial common implementation
chilagrow Jul 20, 2023
c4073da
rename
chilagrow Jul 20, 2023
d0c0334
distinct uses new impl
chilagrow Jul 20, 2023
33936ff
use new function in filter
chilagrow Jul 20, 2023
aea192d
some cleaning
chilagrow Jul 20, 2023
4594b67
update doc
chilagrow Jul 20, 2023
3a693bd
re-organise filter pair code
chilagrow Jul 20, 2023
1ef1ef9
handle empty key for filter
chilagrow Jul 20, 2023
bee2816
add tests, comments and refactor
chilagrow Jul 21, 2023
d1b8ff0
tidy up
chilagrow Jul 21, 2023
2e0eff7
Merge branch 'main' into issue-2348-get-path-value
chilagrow Jul 21, 2023
9e188b5
fix merge
chilagrow Jul 21, 2023
5ce3f80
fix merge
chilagrow Jul 21, 2023
7b65633
reorganise
chilagrow Jul 21, 2023
876744d
Merge branch 'main' into issue-2348-get-path-value
mergify[bot] Jul 24, 2023
292d897
Merge branch 'main' into issue-2348-get-path-value
mergify[bot] Jul 24, 2023
d0be65d
Merge branch 'main' into issue-2348-get-path-value
mergify[bot] Jul 24, 2023
cc4af16
Merge branch 'main' into issue-2348-get-path-value
mergify[bot] Jul 24, 2023
15aaf03
Merge branch 'main' into issue-2348-get-path-value
mergify[bot] Jul 24, 2023
faf7daa
Merge branch 'main' into issue-2348-get-path-value
mergify[bot] Jul 24, 2023
ae4f67d
Refactor `types.Path` a bit
AlekSi Jul 24, 2023
fc446a3
Small tweaks
AlekSi Jul 24, 2023
0313d17
update test
chilagrow Jul 26, 2023
51c106b
merge
chilagrow Jul 26, 2023
1d5051f
checkpoint
chilagrow Jul 26, 2023
20c464a
Tweak documentation a little bit
AlekSi Jul 26, 2023
aa74542
another checkpoint
chilagrow Jul 26, 2023
7bf0bdd
Merge branch 'issue-2348-get-path-value' of github.com:chilagrow/Ferr…
chilagrow Jul 26, 2023
789bae3
some update
chilagrow Jul 26, 2023
2885c85
more comment
chilagrow Jul 27, 2023
76f0724
projection returns correct error code
chilagrow Jul 27, 2023
b31332f
fmt
chilagrow Jul 27, 2023
85f509d
Merge branch 'main' into issue-2348-get-path-value
chilagrow Jul 27, 2023
74d4fc1
path does not return error on $ prefix
chilagrow Jul 27, 2023
ca376f4
Revert "projection returns correct error code"
chilagrow Jul 27, 2023
8147dfe
add todo for fixing projection positional operator
chilagrow Jul 27, 2023
e2fc0ff
aggregation projection checks valid path
chilagrow Jul 27, 2023
bac5eeb
add todo
chilagrow Jul 27, 2023
9f19a76
fmt
chilagrow Jul 27, 2023
8a41dc4
Merge branch 'main' into issue-2348-get-path-value
chilagrow Jul 27, 2023
d7c16a0
Revert "aggregation projection checks valid path"
chilagrow Jul 28, 2023
7e49bd4
review comment
chilagrow Jul 28, 2023
3811585
renaming
chilagrow Jul 28, 2023
bc74089
test empty and space keys for insert and find
chilagrow Jul 28, 2023
1316e0c
Revert "test empty and space keys for insert and find"
chilagrow Jul 28, 2023
ea14b20
clean up
chilagrow Jul 28, 2023
b047e14
Merge branch 'main' into issue-2348-get-path-value
AlekSi Aug 1, 2023
e00bd95
update expression documentation
chilagrow Aug 1, 2023
9c4899c
Merge branch 'issue-2348-get-path-value' of github.com:chilagrow/Ferr…
chilagrow Aug 1, 2023
b706f91
update comment
chilagrow Aug 1, 2023
aa3fecd
Merge branch 'main' into issue-2348-get-path-value
chilagrow Aug 2, 2023
f2f0106
update expression comments
chilagrow Aug 2, 2023
b93ea91
more comments
chilagrow Aug 2, 2023
8b205c2
from pairing session
chilagrow Aug 2, 2023
7c0c76f
add more comments
chilagrow Aug 3, 2023
fd19a7a
more update of inline comments
chilagrow Aug 3, 2023
76a63cf
minor comment update
chilagrow Aug 3, 2023
307792e
resolve merge conflict
chilagrow Aug 4, 2023
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
Prev Previous commit
Next Next commit
Refactor types.Path a bit
  • Loading branch information
AlekSi committed Jul 24, 2023
commit ae4f67d067f348905eaf3f13c1149c9a82c55b36
2 changes: 1 addition & 1 deletion integration/compattestcaseresulttype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion integration/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
"github.com/FerretDB/FerretDB/internal/util/testutil/testtb"
)

//go:generate ../bin/stringer -type compatTestCaseResultType
//go:generate ../bin/stringer -linecomment -type compatTestCaseResultType

// compatTestCaseResultType represents compatibility test case result type.
//
Expand Down
2 changes: 1 addition & 1 deletion internal/backends/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/FerretDB/FerretDB/internal/util/debugbuild"
)

//go:generate ../../bin/stringer -type ErrorCode
//go:generate ../../bin/stringer -linecomment -type ErrorCode

// ErrorCode represent a backend error code.
type ErrorCode int
Expand Down
2 changes: 1 addition & 1 deletion internal/backends/errorcode_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 3 additions & 13 deletions internal/handlers/common/aggregations/stages/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package stages

import (
"context"
"errors"

"github.com/FerretDB/FerretDB/internal/handlers/common"
"github.com/FerretDB/FerretDB/internal/handlers/common/aggregations"
Expand Down Expand Up @@ -59,19 +58,10 @@ func newSort(stage *types.Document) (aggregations.Stage, error) {

// Process implements Stage interface.
//
// If sort path is invalid, it returns a possibly wrapped types.DocumentPathError.
// If sort path is invalid, it returns a possibly wrapped types.PathError.
func (s *sort) Process(ctx context.Context, iter types.DocumentsIterator, closer *iterator.MultiCloser) (types.DocumentsIterator, error) { //nolint:lll // for readability
var err error
if iter, err = common.SortIterator(iter, closer, s.fields); err != nil {
var pathErr *types.DocumentPathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrDocumentPathEmptyKey {
return nil, commonerrors.NewCommandErrorMsgWithArgument(
commonerrors.ErrPathContainsEmptyElement,
"FieldPath field names may not be empty strings.",
"$sort (stage)",
)
}

iter, err := common.SortIterator(iter, closer, s.fields)
if err != nil {
return nil, lazyerrors.Error(err)
}

Expand Down
6 changes: 3 additions & 3 deletions internal/handlers/common/aggregations/stages/unset.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ func newUnset(stage *types.Document) (aggregations.Stage, error) {
}

err = types.IsConflictPath(visitedPaths, *path)
var pathErr *types.DocumentPathError
var pathErr *types.PathError

if errors.As(err, &pathErr) {
if pathErr.Code() == types.ErrDocumentPathConflictOverwrite {
if pathErr.Code() == types.ErrPathConflictOverwrite {
// the path overwrites one of visitedPaths.
return nil, commonerrors.NewCommandErrorMsgWithArgument(
commonerrors.ErrUnsetPathOverwrite,
Expand All @@ -97,7 +97,7 @@ func newUnset(stage *types.Document) (aggregations.Stage, error) {
)
}

if pathErr.Code() == types.ErrDocumentPathConflictCollision {
if pathErr.Code() == types.ErrPathConflictCollision {
// the path creates collision at one of visitedPaths.
return nil, commonerrors.NewCommandErrorMsgWithArgument(
commonerrors.ErrUnsetPathCollision,
Expand Down
2 changes: 1 addition & 1 deletion internal/handlers/common/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (

// SortDocuments sorts given documents in place according to the given sorting conditions.
//
// If sort path is invalid, it returns a possibly wrapped types.DocumentPathError.
// If sort path is invalid, it returns a possibly wrapped types.PathError.
func SortDocuments(docs []*types.Document, sortDoc *types.Document) error {
if sortDoc.Len() == 0 {
return nil
Expand Down
16 changes: 8 additions & 8 deletions internal/handlers/common/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,8 @@ func processRenameFieldExpression(command string, doc *types.Document, update *t

sourcePath, err := types.NewPathFromString(key)
if err != nil {
var pathErr *types.DocumentPathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrDocumentPathEmptyKey {
var pathErr *types.PathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrPathElementEmpty {
return false, newUpdateError(
commonerrors.ErrEmptyName,
fmt.Sprintf(
Expand All @@ -281,16 +281,16 @@ func processRenameFieldExpression(command string, doc *types.Document, update *t
// Get value to move
val, err := doc.GetByPath(sourcePath)
if err != nil {
var dpe *types.DocumentPathError
var dpe *types.PathError
if !errors.As(err, &dpe) {
panic("getByPath returned error with invalid type")
}

if dpe.Code() == types.ErrDocumentPathKeyNotFound || dpe.Code() == types.ErrDocumentPathIndexOutOfBound {
if dpe.Code() == types.ErrPathKeyNotFound || dpe.Code() == types.ErrPathIndexOutOfBound {
continue
}

if dpe.Code() == types.ErrDocumentPathArrayInvalidIndex {
if dpe.Code() == types.ErrPathIndexInvalid {
return false, newUpdateError(
commonerrors.ErrUnsuitableValueType,
fmt.Sprintf("cannot use path '%s' to traverse the document", sourcePath),
Expand Down Expand Up @@ -918,11 +918,11 @@ func validateOperatorKeys(command string, docs ...*types.Document) error {
}

err = types.IsConflictPath(visitedPaths, nextPath)
var pathErr *types.DocumentPathError
var pathErr *types.PathError

if errors.As(err, &pathErr) {
if pathErr.Code() == types.ErrDocumentPathConflictOverwrite ||
pathErr.Code() == types.ErrDocumentPathConflictCollision {
if pathErr.Code() == types.ErrPathConflictOverwrite ||
pathErr.Code() == types.ErrPathConflictCollision {
return newUpdateError(
commonerrors.ErrConflictingUpdateOperators,
fmt.Sprintf(
Expand Down
4 changes: 2 additions & 2 deletions internal/handlers/pg/msg_find.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ func (h *Handler) MsgFind(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, er
if !queryRes.SortPushdown {
iter, err = common.SortIterator(iter, closer, params.Sort)
if err != nil {
var pathErr *types.DocumentPathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrDocumentPathEmptyKey {
var pathErr *types.PathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrPathElementEmpty {
return commonerrors.NewCommandErrorMsgWithArgument(
commonerrors.ErrPathContainsEmptyElement,
"Empty field names in path are not allowed",
Expand Down
4 changes: 2 additions & 2 deletions internal/handlers/pg/msg_findandmodify.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ func (h *Handler) MsgFindAndModify(ctx context.Context, msg *wire.OpMsg) (*wire.
}

if err = common.SortDocuments(resDocs, params.Sort); err != nil {
var pathErr *types.DocumentPathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrDocumentPathEmptyKey {
var pathErr *types.PathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrPathElementEmpty {
return commonerrors.NewCommandErrorMsgWithArgument(
commonerrors.ErrPathContainsEmptyElement,
"FieldPath field names may not be empty strings.",
Expand Down
6 changes: 3 additions & 3 deletions internal/handlers/pg/pgdb/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func prepareWhereClause(p *Placeholder, sqlFilters *types.Document) (string, []a

path, err := types.NewPathFromString(rootKey)

var pe *types.DocumentPathError
var pe *types.PathError

switch {
case err == nil:
Expand All @@ -264,11 +264,11 @@ func prepareWhereClause(p *Placeholder, sqlFilters *types.Document) (string, []a
}
case errors.As(err, &pe):
// ignore empty key error, otherwise return error
if pe.Code() != types.ErrDocumentPathEmptyKey {
if pe.Code() != types.ErrPathElementEmpty {
return "", nil, lazyerrors.Error(err)
}
default:
panic("Invalid error type: DocumentPathError expected ")
panic("Invalid error type: PathError expected")
}

switch v := rootVal.(type) {
Expand Down
4 changes: 2 additions & 2 deletions internal/handlers/sqlite/msg_find.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ func (h *Handler) MsgFind(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, er
if err != nil {
closer.Close()

var pathErr *types.DocumentPathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrDocumentPathEmptyKey {
var pathErr *types.PathError
if errors.As(err, &pathErr) && pathErr.Code() == types.ErrPathElementEmpty {
return nil, commonerrors.NewCommandErrorMsgWithArgument(
commonerrors.ErrPathContainsEmptyElement,
"Empty field names in path are not allowed",
Expand Down
31 changes: 0 additions & 31 deletions internal/types/documentpatherrorcode_string.go

This file was deleted.

109 changes: 109 additions & 0 deletions internal/types/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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 types

import (
"fmt"
"math"
"strings"
"time"
)

// FormatAnyValue formats value for error message output.
func FormatAnyValue(v any) string {
switch v := v.(type) {
case *Document:
return formatDocument(v)
case *Array:
return formatArray(v)
case float64:
switch {
case math.IsNaN(v):
return "nan.0"

case math.IsInf(v, -1):
return "-inf.0"
case math.IsInf(v, +1):
return "inf.0"
case v == 0 && math.Signbit(v):
return "-0.0"
case v == 0.0:
return "0.0"
case v > 1000 || v < -1000 || v == math.SmallestNonzeroFloat64:
return fmt.Sprintf("%.15e", v)
case math.Trunc(v) == v:
return fmt.Sprintf("%d.0", int64(v))
default:
res := fmt.Sprintf("%.2f", v)

return strings.TrimSuffix(res, "0")
}

case string:
return fmt.Sprintf(`"%v"`, v)
case Binary:
return fmt.Sprintf("BinData(%d, %X)", v.Subtype, v.B)
case ObjectID:
return fmt.Sprintf("ObjectId('%x')", v)
case bool:
return fmt.Sprintf("%v", v)
case time.Time:
return fmt.Sprintf("new Date(%d)", v.UnixMilli())
case NullType:
return "null"
case Regex:
return fmt.Sprintf("/%s/%s", v.Pattern, v.Options)
case int32:
return fmt.Sprintf("%d", v)
case Timestamp:
return fmt.Sprintf("Timestamp(%v, %v)", int64(v)>>32, int32(v))
case int64:
return fmt.Sprintf("%d", v)
default:
panic(fmt.Sprintf("unknown type %T", v))
}
}

// formatDocument formats Document for error output.
func formatDocument(doc *Document) string {
result := "{ "

for i, f := range doc.fields {
if i > 0 {
result += ", "
}

result += fmt.Sprintf("%s: %s", f.key, FormatAnyValue(f.value))
}

return result + " }"
}

// formatArray formats Array for error output.
func formatArray(array *Array) string {
if len(array.s) == 0 {
return "[]"
}

result := "[ "

for _, elem := range array.s {
result += fmt.Sprintf("%s, ", FormatAnyValue(elem))
}

result = strings.TrimSuffix(result, ", ")

return result + " ]"
}
Loading