diff --git a/DEPS.bzl b/DEPS.bzl index 9e868da42f33..e0877254d04e 100644 --- a/DEPS.bzl +++ b/DEPS.bzl @@ -1363,8 +1363,8 @@ def go_deps(): name = "com_github_jackc_pgconn", build_file_proto_mode = "disable_global", importpath = "github.com/jackc/pgconn", - sum = "h1:lwofaXKPbIx6qEaK8mNm7uZuOwxHw+PnAFGDsDFpkRI=", - version = "v1.6.1", + sum = "h1:FmjZ0rOyXTr1wfWs45i4a9vjnjWUAGpMuQLD9OSs+lw=", + version = "v1.8.0", ) go_repository( name = "com_github_jackc_pgio", @@ -1398,15 +1398,15 @@ def go_deps(): name = "com_github_jackc_pgproto3_v2", build_file_proto_mode = "disable_global", importpath = "github.com/jackc/pgproto3/v2", - sum = "h1:RHkX5ZUD9bl/kn0f9dYUWs1N7Nwvo1wwUYvKiR26Zco=", - version = "v2.0.4", + sum = "h1:6Pwi1b3QdY65cuv6SyVO0FgPd5J3Bl7wf/nQQjinHMA=", + version = "v2.0.7", ) go_repository( name = "com_github_jackc_pgservicefile", build_file_proto_mode = "disable_global", importpath = "github.com/jackc/pgservicefile", - sum = "h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU=", - version = "v0.0.0-20200307190119-3430c5407db8", + sum = "h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=", + version = "v0.0.0-20200714003250-2b9c44734f2b", ) go_repository( name = "com_github_jackc_pgtype", @@ -2915,8 +2915,8 @@ def go_deps(): name = "org_golang_x_crypto", build_file_proto_mode = "disable_global", importpath = "golang.org/x/crypto", - sum = "h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=", - version = "v0.0.0-20200622213623-75b288015ac9", + sum = "h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=", + version = "v0.0.0-20201221181555-eec23a3978ad", ) go_repository( name = "org_golang_x_exp", @@ -2988,12 +2988,20 @@ def go_deps(): sum = "h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=", version = "v0.0.0-20210108172913-0df2131ae363", ) + go_repository( + name = "org_golang_x_term", + build_file_proto_mode = "disable_global", + importpath = "golang.org/x/term", + sum = "h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=", + version = "v0.0.0-20201117132131-f5c789dd3221", + ) + go_repository( name = "org_golang_x_text", build_file_proto_mode = "disable_global", importpath = "golang.org/x/text", - sum = "h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=", - version = "v0.3.3", + sum = "h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=", + version = "v0.3.5", ) go_repository( name = "org_golang_x_time", diff --git a/go.mod b/go.mod index 3b69f4f094c6..61d3c0ad7d0d 100644 --- a/go.mod +++ b/go.mod @@ -87,8 +87,8 @@ require ( github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 github.com/ianlancetaylor/cgosymbolizer v0.0.0-20201002210021-dda951febc36 // indirect github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect - github.com/jackc/pgconn v1.6.1 - github.com/jackc/pgproto3/v2 v2.0.4 + github.com/jackc/pgconn v1.8.0 + github.com/jackc/pgproto3/v2 v2.0.7 github.com/jackc/pgx v3.6.2+incompatible github.com/jackc/pgx/v4 v4.6.0 github.com/jaegertracing/jaeger v1.17.0 @@ -146,7 +146,7 @@ require ( github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad github.com/zabawaba99/go-gitignore v0.0.0-20200117185801-39e6bddfb292 go.etcd.io/etcd/raft/v3 v3.0.0-20201109164711-01844fd28560 - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/exp v0.0.0-20201229011636-eab1b5eb1a03 golang.org/x/lint v0.0.0-20200130185559-910be7a94367 golang.org/x/net v0.0.0-20201021035429-f5854403a974 @@ -154,7 +154,8 @@ require ( golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 - golang.org/x/text v0.3.3 + golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 + golang.org/x/text v0.3.5 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee google.golang.org/api v0.1.0 diff --git a/go.sum b/go.sum index 5f1780ab85f8..296aaafe91c2 100644 --- a/go.sum +++ b/go.sum @@ -405,8 +405,8 @@ github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9 github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.6.1 h1:lwofaXKPbIx6qEaK8mNm7uZuOwxHw+PnAFGDsDFpkRI= -github.com/jackc/pgconn v1.6.1/go.mod h1:g8mKMqmSUO6AzAvha7vy07g1rbGOlc7iF0nU0ei83hc= +github.com/jackc/pgconn v1.8.0 h1:FmjZ0rOyXTr1wfWs45i4a9vjnjWUAGpMuQLD9OSs+lw= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= @@ -420,11 +420,14 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.4 h1:RHkX5ZUD9bl/kn0f9dYUWs1N7Nwvo1wwUYvKiR26Zco= -github.com/jackc/pgproto3/v2 v2.0.4/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.7 h1:6Pwi1b3QdY65cuv6SyVO0FgPd5J3Bl7wf/nQQjinHMA= +github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -761,6 +764,8 @@ golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= @@ -848,6 +853,7 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -857,10 +863,14 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s= golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/cmd/roachprod/BUILD.bazel b/pkg/cmd/roachprod/BUILD.bazel index a107f1d8251d..c69a6cf46213 100644 --- a/pkg/cmd/roachprod/BUILD.bazel +++ b/pkg/cmd/roachprod/BUILD.bazel @@ -23,8 +23,8 @@ go_library( "//pkg/util/flagutil", "@com_github_cockroachdb_errors//:errors", "@com_github_spf13_cobra//:cobra", - "@org_golang_x_crypto//ssh/terminal", "@org_golang_x_sys//unix", + "@org_golang_x_term//:term", ], ) diff --git a/pkg/cmd/roachprod/main.go b/pkg/cmd/roachprod/main.go index 10791c8932c1..7378bac5a43d 100644 --- a/pkg/cmd/roachprod/main.go +++ b/pkg/cmd/roachprod/main.go @@ -41,8 +41,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/flagutil" "github.com/cockroachdb/errors" "github.com/spf13/cobra" - "golang.org/x/crypto/ssh/terminal" "golang.org/x/sys/unix" + "golang.org/x/term" ) var rootCmd = &cobra.Command{ @@ -185,7 +185,7 @@ Available clusters: c.Tag = "/" + tag } c.UseTreeDist = useTreeDist - c.Quiet = quiet || !terminal.IsTerminal(int(os.Stdout.Fd())) + c.Quiet = quiet || !term.IsTerminal(int(os.Stdout.Fd())) c.MaxConcurrency = maxConcurrency return c, nil } diff --git a/pkg/security/BUILD.bazel b/pkg/security/BUILD.bazel index 114410c6db92..4e8edf7523b8 100644 --- a/pkg/security/BUILD.bazel +++ b/pkg/security/BUILD.bazel @@ -36,8 +36,8 @@ go_library( "@com_github_cockroachdb_redact//:redact", "@org_golang_x_crypto//bcrypt", "@org_golang_x_crypto//ocsp", - "@org_golang_x_crypto//ssh/terminal", "@org_golang_x_sync//errgroup", + "@org_golang_x_term//:term", ], ) diff --git a/pkg/security/password.go b/pkg/security/password.go index 450846476383..dd6d6f9ea5b6 100644 --- a/pkg/security/password.go +++ b/pkg/security/password.go @@ -18,7 +18,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/errors" "golang.org/x/crypto/bcrypt" - "golang.org/x/crypto/ssh/terminal" + "golang.org/x/term" ) // BcryptCost is the cost to use when hashing passwords. It is exposed for @@ -69,7 +69,7 @@ func HashPassword(password string) ([]byte, error) { // This is meant to be used when using a password. func PromptForPassword() (string, error) { fmt.Print("Enter password: ") - password, err := terminal.ReadPassword(int(os.Stdin.Fd())) + password, err := term.ReadPassword(int(os.Stdin.Fd())) if err != nil { return "", err } diff --git a/pkg/sql/logictest/testdata/logic_test/inverted_index b/pkg/sql/logictest/testdata/logic_test/inverted_index index a17ad13723fc..6a499bb2cd24 100644 --- a/pkg/sql/logictest/testdata/logic_test/inverted_index +++ b/pkg/sql/logictest/testdata/logic_test/inverted_index @@ -753,6 +753,22 @@ SELECT j FROM f@i WHERE j->'a' = '1' ORDER BY k {"a": 1, "b": 2} {"a": 1, "c": 3} +query T +SELECT j FROM f@i WHERE j->'a' = '1' OR j->'b' = '2' ORDER BY k +---- +{"a": 1} +{"b": 2} +{"a": 1, "b": 2} +{"a": 1, "c": 3} + +query T +SELECT j FROM f@i WHERE j->'a' = '1' OR j @> '{"b": 2}' ORDER BY k +---- +{"a": 1} +{"b": 2} +{"a": 1, "b": 2} +{"a": 1, "c": 3} + subtest arrays statement ok diff --git a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index index de463df0a567..5aad23dfada5 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index +++ b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index @@ -350,7 +350,7 @@ vectorized: true │ └── • scan columns: (a) - estimated row count: 110 (missing stats) + estimated row count: 111 (missing stats) table: d@foo_inv spans: /"a"/"b"-/"a"/"b"/PrefixEnd @@ -399,7 +399,7 @@ vectorized: true │ └── • scan columns: (a) - estimated row count: 110 (missing stats) + estimated row count: 111 (missing stats) table: d@foo_inv spans: /"a"/"b"-/"a"/"b"/PrefixEnd diff --git a/pkg/sql/opt/invertedidx/BUILD.bazel b/pkg/sql/opt/invertedidx/BUILD.bazel index e7d90cd74836..c9b1e4f654a4 100644 --- a/pkg/sql/opt/invertedidx/BUILD.bazel +++ b/pkg/sql/opt/invertedidx/BUILD.bazel @@ -28,6 +28,7 @@ go_library( "//pkg/sql/sem/tree", "//pkg/sql/types", "//pkg/util/encoding", + "//pkg/util/json", "@com_github_cockroachdb_errors//:errors", "@com_github_golang_geo//r1", "@com_github_golang_geo//s1", diff --git a/pkg/sql/opt/invertedidx/geo_test.go b/pkg/sql/opt/invertedidx/geo_test.go index b2069c3590d9..2b3b4e96e0bd 100644 --- a/pkg/sql/opt/invertedidx/geo_test.go +++ b/pkg/sql/opt/invertedidx/geo_test.go @@ -491,7 +491,13 @@ func TestTryFilterGeoIndex(t *testing.T) { // that is tested elsewhere. This is just testing that we are constraining // the index when we expect to. spanExpr, _, remainingFilters, pfState, ok := invertedidx.TryFilterInvertedIndex( - evalCtx, &f, filters, nil /* optionalFilters */, tab, md.Table(tab).Index(tc.indexOrd), + evalCtx, + &f, + filters, + nil, /* optionalFilters */ + tab, + md.Table(tab).Index(tc.indexOrd), + nil, /* computedColumns */ ) if tc.ok != ok { t.Fatalf("expected %v, got %v", tc.ok, ok) diff --git a/pkg/sql/opt/invertedidx/inverted_index_expr.go b/pkg/sql/opt/invertedidx/inverted_index_expr.go index b996aa8beb90..bb42f86fde51 100644 --- a/pkg/sql/opt/invertedidx/inverted_index_expr.go +++ b/pkg/sql/opt/invertedidx/inverted_index_expr.go @@ -72,6 +72,7 @@ func TryFilterInvertedIndex( optionalFilters memo.FiltersExpr, tabID opt.TableID, index cat.Index, + computedColumns map[opt.ColumnID]opt.ScalarExpr, ) ( spanExpr *invertedexpr.SpanExpression, constraint *constraint.Constraint, @@ -109,8 +110,9 @@ func TryFilterInvertedIndex( typ = types.Geometry } else { filterPlanner = &jsonOrArrayFilterPlanner{ - tabID: tabID, - index: index, + tabID: tabID, + index: index, + computedColumns: computedColumns, } col := index.VirtualInvertedColumn().InvertedSourceColumnOrdinal() typ = factory.Metadata().Table(tabID).Column(col).DatumType() @@ -497,3 +499,22 @@ func extractInvertedFilterCondition( return filterPlanner.extractInvertedFilterConditionFromLeaf(evalCtx, filterCond) } } + +// isIndexColumn returns true if e is an expression that corresponds to an +// inverted index column. The expression can be either: +// - a variable on the index column, or +// - an expression that matches the computed column expression (if the index +// column is computed). +// +func isIndexColumn( + tabID opt.TableID, index cat.Index, e opt.Expr, computedColumns map[opt.ColumnID]opt.ScalarExpr, +) bool { + invertedSourceCol := tabID.ColumnID(index.VirtualInvertedColumn().InvertedSourceColumnOrdinal()) + if v, ok := e.(*memo.VariableExpr); ok && v.Col == invertedSourceCol { + return true + } + if computedColumns != nil && e == computedColumns[invertedSourceCol] { + return true + } + return false +} diff --git a/pkg/sql/opt/invertedidx/json_array.go b/pkg/sql/opt/invertedidx/json_array.go index bf2a0a6d4d7f..48b12a166c43 100644 --- a/pkg/sql/opt/invertedidx/json_array.go +++ b/pkg/sql/opt/invertedidx/json_array.go @@ -23,6 +23,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/rowenc" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/cockroach/pkg/util/json" "github.com/cockroachdb/errors" ) @@ -56,19 +57,15 @@ func (j *jsonOrArrayJoinPlanner) extractInvertedJoinConditionFromLeaf( func (j *jsonOrArrayJoinPlanner) canExtractJSONOrArrayJoinCondition( left, right opt.ScalarExpr, ) bool { - // The first argument should be a variable corresponding to the index - // column. - variable, ok := left.(*memo.VariableExpr) - if !ok { - return false - } - if variable.Col != j.tabID.ColumnID( - j.index.VirtualInvertedColumn().InvertedSourceColumnOrdinal(), - ) { - // The column does not match the index column. + // The first argument should be a variable or expression corresponding to + // the index column. + // TODO(mgartner): The first argument could be an expression that matches a + // computed column expression if the computed column is indexed. Pass + // computedColumns to enable this. + if !isIndexColumn(j.tabID, j.index, left, nil /* computedColumns */) { return false } - if variable.Typ.Family() == types.ArrayFamily && + if left.DataType().Family() == types.ArrayFamily && j.index.Version() < descpb.EmptyArraysInInvertedIndexesVersion { // We cannot plan inverted joins on array indexes that do not include // keys for empty arrays. @@ -259,8 +256,9 @@ func (g *jsonOrArrayDatumsToInvertedExpr) PreFilter( } type jsonOrArrayFilterPlanner struct { - tabID opt.TableID - index cat.Index + tabID opt.TableID + index cat.Index + computedColumns map[opt.ColumnID]opt.ScalarExpr } var _ invertedFilterPlanner = &jsonOrArrayFilterPlanner{} @@ -275,39 +273,40 @@ func (j *jsonOrArrayFilterPlanner) extractInvertedFilterConditionFromLeaf( _ *invertedexpr.PreFiltererStateForInvertedFilterer, ) { switch t := expr.(type) { - // TODO(rytaft): Support JSON fetch val operator (->). case *memo.ContainsExpr: - invertedExpr := j.extractJSONOrArrayFilterCondition(evalCtx, t.Left, t.Right) - if !invertedExpr.IsTight() { - remainingFilters = expr + invertedExpr = j.extractJSONOrArrayContainsCondition(evalCtx, t.Left, t.Right) + case *memo.EqExpr: + if fetch, ok := t.Left.(*memo.FetchValExpr); ok { + invertedExpr = j.extractJSONFetchValEqCondition(evalCtx, fetch, t.Right) } + } - // We do not currently support pre-filtering for JSON and Array indexes, so - // the returned pre-filter state is nil. - return invertedExpr, remainingFilters, nil - - default: + if invertedExpr == nil { + // An inverted expression could not be extracted. return invertedexpr.NonInvertedColExpression{}, expr, nil } + + // If the extracted inverted expression is not tight then remaining filters + // must be applied after the inverted index scan. + if !invertedExpr.IsTight() { + remainingFilters = expr + } + + // We do not currently support pre-filtering for JSON and Array indexes, so + // the returned pre-filter state is nil. + return invertedExpr, remainingFilters, nil } -// extractJSONOrArrayFilterCondition extracts an InvertedExpression -// representing an inverted filter over the given inverted index, based +// extractJSONOrArrayContainsCondition extracts an InvertedExpression +// representing an inverted filter over the planner's inverted index, based // on the given left and right expression arguments. Returns an empty // InvertedExpression if no inverted filter could be extracted. -func (j *jsonOrArrayFilterPlanner) extractJSONOrArrayFilterCondition( +func (j *jsonOrArrayFilterPlanner) extractJSONOrArrayContainsCondition( evalCtx *tree.EvalContext, left, right opt.ScalarExpr, ) invertedexpr.InvertedExpression { - // The first argument should be a variable corresponding to the index - // column. - variable, ok := left.(*memo.VariableExpr) - if !ok { - return invertedexpr.NonInvertedColExpression{} - } - if variable.Col != j.tabID.ColumnID( - j.index.VirtualInvertedColumn().InvertedSourceColumnOrdinal(), - ) { - // The column does not match the index column. + // The first argument should be a variable or expression corresponding to + // the index column. + if !isIndexColumn(j.tabID, j.index, left, j.computedColumns) { return invertedexpr.NonInvertedColExpression{} } @@ -316,7 +315,7 @@ func (j *jsonOrArrayFilterPlanner) extractJSONOrArrayFilterCondition( return invertedexpr.NonInvertedColExpression{} } d := memo.ExtractConstDatum(right) - if variable.Typ.Family() == types.ArrayFamily && + if left.DataType().Family() == types.ArrayFamily && j.index.Version() < descpb.EmptyArraysInInvertedIndexesVersion { if arr, ok := d.(*tree.DArray); ok && arr.Len() == 0 { // We cannot constrain array indexes that do not include @@ -327,3 +326,56 @@ func (j *jsonOrArrayFilterPlanner) extractJSONOrArrayFilterCondition( return getSpanExprForJSONOrArrayIndex(evalCtx, d) } + +// extractJSONFetchValEqCondition extracts an InvertedExpression representing an +// inverted filter over the planner's inverted index, based on equality between +// a fetch val expression and a right scalar expression. If the following criteria +// are not met, an empty InvertedExpression is returned. +// +// 1. The fetch value operator's left expression must be a variable +// referencing the inverted column in the index. +// 2. The fetch value operator's right expression must be a constant string. +// 3. The right expression in the equality expression must be a constant JSON +// value that is not an object or an array. +// +// TODO(mgartner): Support chained fetch val operators, e.g., j->'a'->'b' = '1'. +func (j *jsonOrArrayFilterPlanner) extractJSONFetchValEqCondition( + evalCtx *tree.EvalContext, fetch *memo.FetchValExpr, right opt.ScalarExpr, +) invertedexpr.InvertedExpression { + // The left side of the fetch val expression, the Json field, should be a + // variable corresponding to the index column. + if !isIndexColumn(j.tabID, j.index, fetch.Json, j.computedColumns) { + return invertedexpr.NonInvertedColExpression{} + } + + // The right side of the fetch val expression, the Index field, should be a + // constant string. + if !memo.CanExtractConstDatum(fetch.Index) { + return invertedexpr.NonInvertedColExpression{} + } + key, ok := memo.ExtractConstDatum(fetch.Index).(*tree.DString) + if !ok { + return invertedexpr.NonInvertedColExpression{} + } + + // The right side of the equals expression should be a constant JSON value + // that is not an object or array. + if !memo.CanExtractConstDatum(right) { + return invertedexpr.NonInvertedColExpression{} + } + val, ok := memo.ExtractConstDatum(right).(*tree.DJSON) + if !ok { + return invertedexpr.NonInvertedColExpression{} + } + typ := val.JSON.Type() + if typ == json.ObjectJSONType || typ == json.ArrayJSONType { + return invertedexpr.NonInvertedColExpression{} + } + + // Build a new JSON object of the form: {: }. + b := json.NewObjectBuilder(1) + b.Add(string(*key), val.JSON) + obj := tree.NewDJSON(b.Build()) + + return getSpanExprForJSONOrArrayIndex(evalCtx, obj) +} diff --git a/pkg/sql/opt/invertedidx/json_array_test.go b/pkg/sql/opt/invertedidx/json_array_test.go index e6bd6ac6d674..82eed7661bfa 100644 --- a/pkg/sql/opt/invertedidx/json_array_test.go +++ b/pkg/sql/opt/invertedidx/json_array_test.go @@ -391,6 +391,73 @@ func TestTryFilterJsonOrArrayIndex(t *testing.T) { unique: false, remainingFilters: "j @> '[[1, 2]]'", }, + { + filters: "j->'a' = '1'", + indexOrd: jsonOrd, + ok: true, + tight: true, + unique: true, + }, + { + // Integer indexes are not yet supported. + filters: "j->0 = '1'", + indexOrd: jsonOrd, + ok: false, + }, + { + // Arrays on the right side of the equality are not yet supported. + filters: "j->'a' = '[1]'", + indexOrd: jsonOrd, + ok: false, + }, + { + // Objects on the right side of the equality are not yet supported. + filters: `j->'a' = '{"b": "c"}'`, + indexOrd: jsonOrd, + ok: false, + }, + { + // Wrong index ordinal. + filters: "j->'a' = '1'", + indexOrd: arrayOrd, + ok: false, + }, + { + filters: "j->'a' = '1' AND j->'b' = '2'", + indexOrd: jsonOrd, + ok: true, + tight: true, + unique: false, + }, + { + filters: "j->'a' = '1' OR j->'b' = '2'", + indexOrd: jsonOrd, + ok: true, + tight: true, + unique: false, + }, + { + filters: `j->'a' = '1' AND j @> '{"b": "c"}'`, + indexOrd: jsonOrd, + ok: true, + tight: true, + unique: false, + }, + { + filters: `j->'a' = '1' OR j @> '{"b": "c"}'`, + indexOrd: jsonOrd, + ok: true, + tight: true, + unique: false, + }, + { + filters: `j->'a' = '1' AND j @> '[[1, 2]]'`, + indexOrd: jsonOrd, + ok: true, + tight: false, + unique: false, + remainingFilters: "j @> '[[1, 2]]'", + }, } for _, tc := range testCases { @@ -402,7 +469,13 @@ func TestTryFilterJsonOrArrayIndex(t *testing.T) { // the index when we expect to and we have the correct values for tight, // unique, and remainingFilters. spanExpr, _, remainingFilters, _, ok := invertedidx.TryFilterInvertedIndex( - evalCtx, &f, filters, nil /* optionalFilters */, tab, md.Table(tab).Index(tc.indexOrd), + evalCtx, + &f, + filters, + nil, /* optionalFilters */ + tab, + md.Table(tab).Index(tc.indexOrd), + nil, /* computedColumns */ ) if tc.ok != ok { t.Fatalf("expected %v, got %v", tc.ok, ok) diff --git a/pkg/sql/opt/xform/select_funcs.go b/pkg/sql/opt/xform/select_funcs.go index 09b49a3488ab..3769c85df436 100644 --- a/pkg/sql/opt/xform/select_funcs.go +++ b/pkg/sql/opt/xform/select_funcs.go @@ -297,7 +297,6 @@ func (c *CustomFuncs) GenerateConstrainedScans( append(optionalFilters, partitionFilters...), scanPrivate.Table, index.Ordinal(), - false, /* isInverted */ ) if !ok { return @@ -309,7 +308,6 @@ func (c *CustomFuncs) GenerateConstrainedScans( append(optionalFilters, inBetweenFilters...), scanPrivate.Table, index.Ordinal(), - false, /* isInverted */ ) if !ok { panic(errors.AssertionFailedf("in-between filters didn't yield a constraint")) @@ -720,6 +718,7 @@ func (c *CustomFuncs) GenerateInvertedIndexScans( ) { var sb indexScanBuilder sb.init(c, scanPrivate.Table) + tabMeta := c.e.mem.Metadata().TableMeta(scanPrivate.Table) // Generate implicit filters from constraints and computed columns as // optional filters to help constrain an index scan. @@ -731,50 +730,35 @@ func (c *CustomFuncs) GenerateInvertedIndexScans( var iter scanIndexIter iter.Init(c.e.mem, &c.im, scanPrivate, filters, rejectNonInvertedIndexes) iter.ForEach(func(index cat.Index, filters memo.FiltersExpr, indexCols opt.ColSet, isCovering bool) { - var spanExpr *invertedexpr.SpanExpression - var pfState *invertedexpr.PreFiltererStateForInvertedFilterer - var spansToRead invertedexpr.InvertedSpans - var constraint *constraint.Constraint - var filterOk, constraintOk bool - // Check whether the filter can constrain the index. - // TODO(rytaft): Unify these two cases so both return a spanExpr. - spanExpr, constraint, remainingFilters, pfState, filterOk := invertedidx.TryFilterInvertedIndex( - c.e.evalCtx, c.e.f, filters, optionalFilters, scanPrivate.Table, index, + spanExpr, constraint, remainingFilters, pfState, ok := invertedidx.TryFilterInvertedIndex( + c.e.evalCtx, c.e.f, filters, optionalFilters, scanPrivate.Table, index, tabMeta.ComputedCols, ) - if filterOk { - spansToRead = spanExpr.SpansToRead - // Override the filters with remainingFilters. If the index is a - // multi-column inverted index, the non-inverted prefix columns are - // constrained by the constraint. It may be possible to reduce the - // filters if the constraint fully describes some of - // sub-expressions. The remainingFilters are the filters that are - // not fully expressed by the constraint. - // - // Consider the example: - // - // CREATE TABLE t (a INT, b INT, g GEOMETRY, INVERTED INDEX (b, g)) - // - // SELECT * FROM t WHERE a = 1 AND b = 2 AND ST_Intersects(.., g) - // - // The constraint would constrain b to [/2 - /2], guaranteeing that - // the inverted index scan would only produce rows where (b = 2). - // Reapplying the (b = 2) filter after the scan would be - // unnecessary, so the remainingFilters in this case would be - // (a = 1 AND ST_Intersects(.., g)). - filters = remainingFilters - } else { - constraint, filters, constraintOk = c.tryConstrainIndex( - filters, - nil, /* optionalFilters */ - scanPrivate.Table, - index.Ordinal(), - true, /* isInverted */ - ) - if !constraintOk { - return - } + if !ok { + // A span expression to constrain the inverted index could not be + // generated. + return } + spansToRead := spanExpr.SpansToRead + // Override the filters with remainingFilters. If the index is a + // multi-column inverted index, the non-inverted prefix columns are + // constrained by the constraint. In this case, it may be possible to + // reduce the filters if the constraint fully describes some of + // sub-expressions. The remainingFilters are the filters that are not + // fully expressed by the constraint. + // + // Consider the example: + // + // CREATE TABLE t (a INT, b INT, g GEOMETRY, INVERTED INDEX (b, g)) + // + // SELECT * FROM t WHERE a = 1 AND b = 2 AND ST_Intersects(.., g) + // + // The constraint would constrain b to [/2 - /2], guaranteeing that + // the inverted index scan would only produce rows where (b = 2). + // Reapplying the (b = 2) filter after the scan would be + // unnecessary, so the remainingFilters in this case would be + // (a = 1 AND ST_Intersects(.., g)). + filters = remainingFilters // Construct new ScanOpDef with the new index and constraint. newScanPrivate := *scanPrivate @@ -786,8 +770,7 @@ func (c *CustomFuncs) GenerateInvertedIndexScans( // produce duplicate primary keys or requires at least one UNION or // INTERSECTION. In this case, we must scan both the primary key columns // and the inverted key column. - needInvertedFilter := spanExpr != nil && - (!spanExpr.Unique || spanExpr.Operator != invertedexpr.None) + needInvertedFilter := !spanExpr.Unique || spanExpr.Operator != invertedexpr.None pkCols := sb.primaryKeyCols() newScanPrivate.Cols = pkCols.Copy() var invertedCol opt.ColumnID @@ -825,19 +808,15 @@ func (c *CustomFuncs) GenerateInvertedIndexScans( // filter remaining after extracting the constraint. If no constraint can be // derived, then tryConstrainIndex returns ok = false. func (c *CustomFuncs) tryConstrainIndex( - requiredFilters, optionalFilters memo.FiltersExpr, - tabID opt.TableID, - indexOrd int, - isInverted bool, + requiredFilters, optionalFilters memo.FiltersExpr, tabID opt.TableID, indexOrd int, ) (constraint *constraint.Constraint, remainingFilters memo.FiltersExpr, ok bool) { // Start with fast check to rule out indexes that cannot be constrained. - if !isInverted && - !c.canMaybeConstrainNonInvertedIndex(requiredFilters, tabID, indexOrd) && + if !c.canMaybeConstrainNonInvertedIndex(requiredFilters, tabID, indexOrd) && !c.canMaybeConstrainNonInvertedIndex(optionalFilters, tabID, indexOrd) { return nil, nil, false } - ic := c.initIdxConstraintForIndex(requiredFilters, optionalFilters, tabID, indexOrd, isInverted) + ic := c.initIdxConstraintForIndex(requiredFilters, optionalFilters, tabID, indexOrd, false /* isInverted */) constraint = ic.Constraint() if constraint.IsUnconstrained() { return nil, nil, false diff --git a/pkg/sql/opt/xform/testdata/rules/select b/pkg/sql/opt/xform/testdata/rules/select index 122b44678a3a..fe0e79ff711b 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -1663,7 +1663,7 @@ project # -------------------------------------------------- # Query only the primary key with no remaining filter. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM b WHERE j @> '{"a": "b"}' ---- project @@ -1676,7 +1676,7 @@ project │ └── spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] └── key: (1) -memo +memo expect=GenerateInvertedIndexScans SELECT k FROM b WHERE j @> '{"a": "b"}' ---- memo (optimized, ~7KB, required=[presentation: k:1]) @@ -1703,7 +1703,7 @@ memo (optimized, ~7KB, required=[presentation: k:1]) └── G9: (const '{"a": "b"}') # Query requiring an index join with no remaining filter. -opt +opt expect=GenerateInvertedIndexScans SELECT u, k FROM b WHERE j @> '{"a": "b"}' ---- project @@ -1722,7 +1722,7 @@ project │ └── spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] └── key: (1) -opt +opt expect=GenerateInvertedIndexScans SELECT j, k FROM b WHERE j @> '{"a": "b"}' ---- index-join b @@ -1736,7 +1736,7 @@ index-join b │ └── spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] └── key: (1) -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM b WHERE j @> '{"a": "b"}' ---- index-join b @@ -1750,7 +1750,7 @@ index-join b │ └── spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] └── key: (1) -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM b WHERE j @> '2' ---- index-join b @@ -1767,7 +1767,7 @@ index-join b └── key: (1) # Disjunction. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM b WHERE j @> '2' OR j @> '1' ---- project @@ -1796,7 +1796,7 @@ project └── fd: (1)-->(6) # Disjunction with non-tight predicate. -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM b WHERE j @> '[[1, 2]]' OR j @> '[[3, 4]]' ---- select @@ -1848,7 +1848,7 @@ select └── filters └── (j:4 @> '[[1, 2]]') OR (j:4 @> '[[3, 4]]') [outer=(4), immutable, constraints=(/4: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM b WHERE j @> '[{}]' ---- index-join b @@ -1873,7 +1873,7 @@ index-join b ├── key: (1) └── fd: (1)-->(6) -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM b WHERE j @> '{"a": {}}' ---- index-join b @@ -1898,7 +1898,7 @@ index-join b ├── key: (1) └── fd: (1)-->(6) -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM b WHERE j @> '{"a": []}' ---- index-join b @@ -1923,7 +1923,7 @@ index-join b ├── key: (1) └── fd: (1)-->(6) -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM b WHERE j @> '{"a":[[{"b":{"c":[{"d":"e"}]}}]]}' ---- index-join b @@ -1937,8 +1937,164 @@ index-join b │ └── spans: ["7a\x00\x02\x00\x03\x00\x03b\x00\x02c\x00\x02\x00\x03d\x00\x01\x12e\x00\x01", "7a\x00\x02\x00\x03\x00\x03b\x00\x02c\x00\x02\x00\x03d\x00\x01\x12e\x00\x01"] └── key: (1) +# Query using the fetch val and equality operators. +opt expect=GenerateInvertedIndexScans +SELECT k FROM b WHERE j->'a' = '"b"' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── scan b@j_inv_idx + ├── columns: k:1!null + ├── inverted constraint: /6/1 + │ └── spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + └── key: (1) + +# Do not generate an inverted scan when the index of the fetch val operator is +# not a string. +opt expect-not=GenerateInvertedIndexScans +SELECT k FROM b WHERE j->0 = '"b"' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── select + ├── columns: k:1!null j:4 + ├── immutable + ├── key: (1) + ├── fd: (1)-->(4) + ├── scan b + │ ├── columns: k:1!null j:4 + │ ├── key: (1) + │ └── fd: (1)-->(4) + └── filters + └── (j:4->0) = '"b"' [outer=(4), immutable] + +# Do not generate an inverted scan when right side of the equality is an array. +opt expect-not=GenerateInvertedIndexScans +SELECT k FROM b WHERE j->'a' = '["b"]' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── select + ├── columns: k:1!null j:4 + ├── immutable + ├── key: (1) + ├── fd: (1)-->(4) + ├── scan b + │ ├── columns: k:1!null j:4 + │ ├── key: (1) + │ └── fd: (1)-->(4) + └── filters + └── (j:4->'a') = '["b"]' [outer=(4), immutable] + +# Do not generate an inverted scan when right side of the equality is an object. +opt expect-not=GenerateInvertedIndexScans +SELECT k FROM b WHERE j->'a' = '{"b": "c"}' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── select + ├── columns: k:1!null j:4 + ├── immutable + ├── key: (1) + ├── fd: (1)-->(4) + ├── scan b + │ ├── columns: k:1!null j:4 + │ ├── key: (1) + │ └── fd: (1)-->(4) + └── filters + └── (j:4->'a') = '{"b": "c"}' [outer=(4), immutable] + +# Query using the fetch val and equality operators in a conjunction. +opt expect=GenerateInvertedIndexScans disable=GenerateInvertedIndexZigzagJoins +SELECT k FROM b WHERE j->'a' = '"b"' AND j->'c' = '"d"' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── inverted-filter + ├── columns: k:1!null + ├── inverted expression: /6 + │ ├── tight: true, unique: false + │ ├── union spans: empty + │ └── INTERSECTION + │ ├── span expression + │ │ ├── tight: true, unique: true + │ │ └── union spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + │ └── span expression + │ ├── tight: true, unique: true + │ └── union spans: ["7c\x00\x01\x12d\x00\x01", "7c\x00\x01\x12d\x00\x01"] + ├── key: (1) + └── scan b@j_inv_idx + ├── columns: k:1!null j_inverted_key:6!null + ├── inverted constraint: /6/1 + │ └── spans + │ ├── ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + │ └── ["7c\x00\x01\x12d\x00\x01", "7c\x00\x01\x12d\x00\x01"] + ├── key: (1) + └── fd: (1)-->(6) + +# Query using the fetch val and equality operators in a disjunction. +opt expect=GenerateInvertedIndexScans +SELECT k FROM b WHERE j->'a' = '"b"' OR j->'c' = '"d"' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── inverted-filter + ├── columns: k:1!null + ├── inverted expression: /6 + │ ├── tight: true, unique: false + │ └── union spans + │ ├── ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + │ └── ["7c\x00\x01\x12d\x00\x01", "7c\x00\x01\x12d\x00\x01"] + ├── key: (1) + └── scan b@j_inv_idx + ├── columns: k:1!null j_inverted_key:6!null + ├── inverted constraint: /6/1 + │ └── spans + │ ├── ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + │ └── ["7c\x00\x01\x12d\x00\x01", "7c\x00\x01\x12d\x00\x01"] + ├── key: (1) + └── fd: (1)-->(6) + +# Query using the fetch val and equality operators in a disjunction with a +# contains operator. +opt expect=GenerateInvertedIndexScans +SELECT k FROM b WHERE j->'a' = '"b"' OR j @> '{"c": "d"}' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── inverted-filter + ├── columns: k:1!null + ├── inverted expression: /6 + │ ├── tight: true, unique: false + │ └── union spans + │ ├── ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + │ └── ["7c\x00\x01\x12d\x00\x01", "7c\x00\x01\x12d\x00\x01"] + ├── key: (1) + └── scan b@j_inv_idx + ├── columns: k:1!null j_inverted_key:6!null + ├── inverted constraint: /6/1 + │ └── spans + │ ├── ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + │ └── ["7c\x00\x01\x12d\x00\x01", "7c\x00\x01\x12d\x00\x01"] + ├── key: (1) + └── fd: (1)-->(6) + # GenerateInvertedIndexScans propagates row-level locking information. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM b WHERE j @> '{"a": "b"}' FOR UPDATE ---- project @@ -1954,7 +2110,7 @@ project └── key: (1) # Tests for array inverted indexes. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM c WHERE a @> ARRAY[1] ---- project @@ -1968,7 +2124,7 @@ project └── key: (1) # Disjunction. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM c WHERE a @> ARRAY[1] OR a @> ARRAY[2] ---- project @@ -1988,7 +2144,7 @@ project ├── key: (1) └── fd: (1)-->(5) -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM c WHERE a @> ARRAY[]::INT[] ---- project @@ -2008,7 +2164,7 @@ project ├── key: (1) └── fd: (1)-->(5) -opt +opt expect-not=GenerateInvertedIndexScans SELECT k FROM c WHERE a IS NULL ---- project @@ -2121,7 +2277,7 @@ CREATE INVERTED INDEX a_inv_idx ON c (a) ---- # Tests for geospatial constrained scans. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Intersects('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog) ---- project @@ -2161,7 +2317,7 @@ project └── st_intersects('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4) [outer=(4), immutable, constraints=(/4: (/NULL - ])] # Same test as above, but with commuted arguments. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Intersects(geog, 'SRID=4326;POINT(-43.23456 72.4567772)'::geography) ---- project @@ -2200,7 +2356,7 @@ project └── filters └── st_intersects(geog:4, '0101000020E61000009279E40F069E45C0BEE36FD63B1D5240') [outer=(4), immutable, constraints=(/4: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_CoveredBy('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, geom) ---- project @@ -2240,7 +2396,7 @@ project └── st_coveredby('0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', geom:3) [outer=(3), immutable, constraints=(/3: (/NULL - ])] # Same test as above, but with commuted arguments. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_CoveredBy(geom, 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry) ---- project @@ -2273,7 +2429,7 @@ project └── filters └── st_coveredby(geom:3, '0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000') [outer=(3), immutable, constraints=(/3: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_DWithin('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, geom, 2) ---- project @@ -2306,7 +2462,7 @@ project └── filters └── st_dwithin('0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', geom:3, 2.0) [outer=(3), immutable, constraints=(/3: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_DFullyWithin('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, geom, 2) ---- project @@ -2339,7 +2495,7 @@ project └── filters └── st_dfullywithin('0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', geom:3, 2.0) [outer=(3), immutable, constraints=(/3: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_DWithin('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog, 2000) ---- project @@ -2392,7 +2548,7 @@ project └── filters └── st_dwithin('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4, 2000.0) [outer=(4), immutable, constraints=(/4: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_DWithin('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog, 2000, false) ---- project @@ -2446,7 +2602,7 @@ project └── st_dwithin('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4, 2000.0, false) [outer=(4), immutable, constraints=(/4: (/NULL - ])] # Test for ST_DWithinExclusive -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_DWithinExclusive('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, geom, 2) ---- project @@ -2480,7 +2636,7 @@ project └── st_dwithinexclusive('0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', geom:3, 2.0) [outer=(3), immutable, constraints=(/3: (/NULL - ])] # Commuted version of previous test. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_DWithinExclusive(geom, 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 2) ---- project @@ -2513,7 +2669,7 @@ project └── filters └── st_dwithinexclusive(geom:3, '0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', 2.0) [outer=(3), immutable, constraints=(/3: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_DFullyWithinExclusive('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, geom, 2) ---- project @@ -2549,7 +2705,7 @@ project # Multiple geospatial functions. # NB: the union spans for the following query are a subset of the spans. This is a # consequence of how intersectSpanExpression does simplification. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Covers('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog) AND ST_CoveredBy('SRID=4326;POINT(-40.23456 70.4567772)'::geography, geog) @@ -2590,7 +2746,7 @@ project ├── st_covers('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4) [outer=(4), immutable, constraints=(/4: (/NULL - ])] └── st_coveredby('0101000020E61000009279E40F061E44C0BEE36FD63B9D5140', geog:4) [outer=(4), immutable, constraints=(/4: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Covers('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog) OR ST_CoveredBy('SRID=4326;POINT(-40.23456 70.4567772)'::geography, geog) @@ -2627,7 +2783,7 @@ project └── filters └── st_covers('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4) OR st_coveredby('0101000020E61000009279E40F061E44C0BEE36FD63B9D5140', geog:4) [outer=(4), immutable, constraints=(/4: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Covers('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog) OR (ST_CoveredBy('SRID=4326;LINESTRING(-118.4079 33.9434, 2.5559 49.0083)'::geography, geog) @@ -2695,7 +2851,7 @@ project └── st_covers('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4) OR (st_coveredby('0102000020E61000000200000075029A081B9A5DC0F085C954C1F840406DC5FEB27B720440454772F90F814840', geog:4) AND st_coveredby('0101000020E610000058569A94821E46C07BC7DFAC773A4E40', geog:4)) [outer=(4), immutable, constraints=(/4: (/NULL - ])] # Multiple geospatial functions on different indexes. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Covers('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog) AND ST_Overlaps('LINESTRING ( 0 0, 0 2 )'::geometry, geom) @@ -2753,7 +2909,7 @@ project └── filters └── st_covers('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4) OR st_touches('0102000000020000000000000000000000000000000000000000000000000000000000000000000040', geom:3) [outer=(3,4), immutable] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Covers('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog) OR (ST_Intersects('SRID=4326;POINT(-40.23456 70.4567772)'::geography, geog) @@ -2792,7 +2948,7 @@ project └── st_covers('0101000020E61000009279E40F069E45C0BEE36FD63B1D5240', geog:4) OR (st_intersects('0101000020E61000009279E40F061E44C0BEE36FD63B9D5140', geog:4) AND st_crosses('0102000000020000000000000000000000000000000000000000000000000000000000000000000040', geom:3)) [outer=(3,4), immutable, constraints=(/4: (/NULL - ])] # Filters on other columns. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE ST_Covers('SRID=4326;POINT(-43.23456 72.4567772)'::geography, geog) AND v = 3 AND k > 100 @@ -2841,7 +2997,7 @@ project └── v:2 = 3 [outer=(2), constraints=(/2: [/3 - /3]; tight), fd=()-->(2)] # Bounding box operations. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE 'BOX(1 2, 3 4)'::box2d ~ geom ---- project @@ -2878,7 +3034,7 @@ project └── filters └── 'BOX(1 2,3 4)' ~ geom:3 [outer=(3), immutable] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE geom ~ 'BOX(1 2, 3 4)'::box2d ---- project @@ -2915,7 +3071,7 @@ project └── filters └── geom:3 ~ 'BOX(1 2,3 4)' [outer=(3), immutable, constraints=(/3: (/NULL - ])] -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM g WHERE geom ~ 'MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry ---- project @@ -2977,7 +3133,7 @@ exec-ddl CREATE INVERTED INDEX idx ON pi (j) WHERE s = 'foo' ---- -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM pi WHERE j @> '{"a": "b"}' AND s = 'foo' ---- project @@ -3000,7 +3156,7 @@ exec-ddl CREATE INVERTED INDEX idx ON pi (j) WHERE j @> '{"group": 1}' ---- -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM pi WHERE j @> '{"a": "b"}' AND j @> '{"group": 1}' ---- project @@ -3023,7 +3179,7 @@ exec-ddl CREATE INVERTED INDEX idx ON pi (j) WHERE s = 'foo' ---- -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM pi WHERE j @> '{"a": "b"}' AND s = 'foo' ---- index-join pi @@ -3047,7 +3203,7 @@ exec-ddl CREATE INVERTED INDEX idx ON pi (j) WHERE s IN ('foo', 'bar') ---- -opt +opt expect=GenerateInvertedIndexScans SELECT * FROM pi WHERE j @> '{"a": "b"}' AND j @> '{"group": 1}' AND s = 'foo' ---- select @@ -3098,7 +3254,7 @@ exec-ddl CREATE INVERTED INDEX idx2 ON pi (j) WHERE s = 'bar' ---- -memo +memo expect=GenerateInvertedIndexScans SELECT * FROM pi WHERE j @> '{"a": "b"}' AND s = 'bar' ---- memo (optimized, ~10KB, required=[presentation: k:1,s:2,j:3]) @@ -3172,7 +3328,7 @@ DROP INDEX idx # Check that we can generate constraints by recognizing computed column # expressions. -opt +opt expect=GenerateInvertedIndexScans SELECT k FROM computed WHERE (j->'foo') @> '{"a": "b"}' ---- project @@ -3181,7 +3337,21 @@ project ├── key: (1) └── scan computed@field_inv_idx ├── columns: k:1!null - ├── constraint: /5/1: [/'{"a": "b"}' - /'{"a": "b"}'] + ├── inverted constraint: /8/1 + │ └── spans: ["7a\x00\x01\x12b\x00\x01", "7a\x00\x01\x12b\x00\x01"] + └── key: (1) + +opt expect=GenerateInvertedIndexScans +SELECT k FROM computed WHERE (j->'foo')->'a' = '1' +---- +project + ├── columns: k:1!null + ├── immutable + ├── key: (1) + └── scan computed@field_inv_idx + ├── columns: k:1!null + ├── inverted constraint: /8/1 + │ └── spans: ["7a\x00\x01*\x02\x00", "7a\x00\x01*\x02\x00"] └── key: (1) # Tests for multi-column inverted indexes. @@ -3687,6 +3857,44 @@ select ├── a:2 = 'foo' [outer=(2), constraints=(/2: [/'foo' - /'foo']; tight), fd=()-->(2)] └── st_coveredby('0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', geom:5) [outer=(5), immutable, constraints=(/5: (/NULL - ])] +# Constrain a prefix computed column when the computed expression matches a +# filter expression. +opt expect=GenerateInvertedIndexScans +SELECT * FROM mc WHERE upper(a) = 'FOO' AND ST_CoveredBy('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, geom) +---- +select + ├── columns: k:1!null a:2!null b:3!null c:4 geom:5!null + ├── immutable + ├── key: (1) + ├── fd: (1)-->(2-5) + ├── index-join mc + │ ├── columns: k:1!null a:2!null b:3!null c:4 geom:5 + │ ├── key: (1) + │ ├── fd: (1)-->(2-5) + │ └── inverted-filter + │ ├── columns: k:1!null + │ ├── inverted expression: /9 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ ├── pre-filterer expression + │ │ └── st_coveredby('0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', geom:5) + │ ├── key: (1) + │ └── scan mc@mc_idx + │ ├── columns: k:1!null geom_inverted_key:9!null + │ ├── constraint: /4: [/'FOO' - /'FOO'] + │ ├── inverted constraint: /9/1 + │ │ └── spans + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ ├── key: (1) + │ └── fd: (1)-->(9) + └── filters + └── st_coveredby('0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000', geom:5) [outer=(5), immutable, constraints=(/5: (/NULL - ])] + exec-ddl DROP INDEX mc_idx ---- @@ -4783,7 +4991,8 @@ SELECT k FROM inv_zz_partial WHERE j->'a' = '1' AND j->'b' = '2' ---- project └── scan inv_zz_partial@zz_idx,partial - └── constraint: /2/1: [/'{"b": 2}' - /'{"b": 2}'] + └── inverted constraint: /7/1 + └── spans: ["7b\x00\x01*\x04\x00", "7b\x00\x01*\x04\x00"] # -------------------------------------------------- # SplitDisjunction diff --git a/pkg/sql/pgwire/testdata/pgtest/notice b/pkg/sql/pgwire/testdata/pgtest/notice index c670417ea373..593395bfd527 100644 --- a/pkg/sql/pgwire/testdata/pgtest/notice +++ b/pkg/sql/pgwire/testdata/pgtest/notice @@ -55,7 +55,7 @@ Query {"String": "DROP INDEX t_x_idx"} until crdb_only CommandComplete ---- -{"Severity":"NOTICE","Code":"00000","Message":"the data for dropped indexes is reclaimed asynchronously","Detail":"","Hint":"The reclamation delay can be customized in the zone configuration for the table.","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"drop_index.go","Line":521,"Routine":"dropIndexByName","UnknownFields":null} +{"Severity":"NOTICE","SeverityUnlocalized":"","Code":"00000","Message":"the data for dropped indexes is reclaimed asynchronously","Detail":"","Hint":"The reclamation delay can be customized in the zone configuration for the table.","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"drop_index.go","Line":521,"Routine":"dropIndexByName","UnknownFields":null} {"Type":"CommandComplete","CommandTag":"DROP INDEX"} until noncrdb_only diff --git a/vendor b/vendor index 0cc7742952de..e1ac11725750 160000 --- a/vendor +++ b/vendor @@ -1 +1 @@ -Subproject commit 0cc7742952de3edf19035cb17fa567dd69ec2c41 +Subproject commit e1ac11725750ec09d1b76a76a8f77c0fa7b8f71c